Repository: qir-alliance/pyqir (language: python)

Issue:
Function objects can outlive their parent module
If a function object outlives its parent module, like in this example:

```python
def foo(bitcode: bytes) -> Function:
    return Module.from_bitcode(bitcode).functions[0]
```

then the returned function is invalid, for example `str(foo(bitcode))` can segfault.


Discussion / hints:

Fix patch:
diff --git a/pyqir/pyqir/__init__.py b/pyqir/pyqir/__init__.py
index f710c117..02127fa9 100644
--- a/pyqir/pyqir/__init__.py
+++ b/pyqir/pyqir/__init__.py
@@ -46,7 +46,6 @@
     result_id,
     result_type,
     result,
-    verify_module,
 )
 
 __all__ = [
@@ -94,5 +93,4 @@
     "result_id",
     "result_type",
     "result",
-    "verify_module",
 ]
diff --git a/pyqir/pyqir/_native.pyi b/pyqir/pyqir/_native.pyi
index 1973e39d..abb009cb 100644
--- a/pyqir/pyqir/_native.pyi
+++ b/pyqir/pyqir/_native.pyi
@@ -189,7 +189,7 @@ class Attribute:
     @property
     def value(self) -> str: ...
 
-def const(ty: Type, value: Union[int, float]) -> Constant: ...
+def const(ty: Type, value: Union[bool, int, float]) -> Constant: ...
 def entry_point(
     module: Module, name: str, required_num_qubits: int, required_num_results: int
 ) -> Function: ...
@@ -340,9 +340,9 @@ class Phi(Instruction):
 class Module:
     def __init__(self, context: Context, name: str) -> None: ...
     @staticmethod
-    def from_ir(ir: str, name: str = "") -> Module: ...
+    def from_ir(context: Context, ir: str, name: str = "") -> Module: ...
     @staticmethod
-    def from_bitcode(bitcode: bytes, name: str = "") -> Module: ...
+    def from_bitcode(context: Context, bitcode: bytes, name: str = "") -> Module: ...
     @property
     def source_filename(self) -> str: ...
     @source_filename.setter
@@ -353,10 +353,9 @@ class Module:
     def bitcode(self) -> bytes: ...
     @property
     def context(self) -> Context: ...
+    def verify(self) -> Optional[str]: ...
     def __str__(self) -> str: ...
 
-def verify_module(module: Module) -> Optional[str]: ...
-
 class PyNonadaptiveJit:
     def eval(
         self,
diff --git a/pyqir/src/builder.rs b/pyqir/src/builder.rs
index 00c0254f..489d913b 100644
--- a/pyqir/src/builder.rs
+++ b/pyqir/src/builder.rs
@@ -2,15 +2,16 @@
 // Licensed under the MIT License.
 
 use crate::{
-    context::{self, Context},
+    context::Context,
     instructions::IntPredicate,
-    values::{self, AnyValue, BasicBlock, Value},
+    types,
+    values::{AnyValue, BasicBlock, Literal, Owner, Value},
 };
 use inkwell::{
-    types::{AnyTypeEnum, FunctionType},
-    values::{AnyValueEnum, BasicValueEnum, CallableValue, IntValue},
+    types::{AnyTypeEnum, BasicTypeEnum, FunctionType},
+    values::{AnyValueEnum, BasicMetadataValueEnum, BasicValueEnum, CallableValue, IntValue},
 };
-use pyo3::{exceptions::PyValueError, prelude::*, types::PySequence};
+use pyo3::{exceptions::PyValueError, prelude::*};
 use qirlib::builder::Ext;
 use std::{
     convert::{Into, TryFrom, TryInto},
@@ -24,7 +25,7 @@ use std::{
 #[pyclass(unsendable)]
 pub(crate) struct Builder {
     builder: inkwell::builder::Builder<'static>,
-    context: Py<Context>,
+    owner: Owner,
 }
 
 #[pymethods]
@@ -41,14 +42,26 @@ impl Builder {
             }
         };
 
-        Self { builder, context }
+        Self {
+            builder,
+            owner: context.into(),
+        }
     }
 
     /// Tells this builder to insert subsequent instructions at the end of the block.
     ///
     /// :param BasicBlock block: The block to insert into.
-    fn insert_at_end(&self, block: &BasicBlock) {
+    fn insert_at_end(&mut self, py: Python, block: PyRef<BasicBlock>) -> PyResult<()> {
+        let owner = block.as_ref().owner();
+        if *owner.context(py).borrow(py) != *self.owner.context(py).borrow(py) {
+            Err(PyValueError::new_err(
+                "Block is not from the same context as builder.",
+            ))?;
+        }
+
+        self.owner = owner.clone_ref(py);
         self.builder.position_at_end(unsafe { block.get() });
+        Ok(())
     }
 
     /// Inserts a bitwise logical and instruction.
@@ -59,13 +72,13 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn and_(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_and::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts a bitwise logical or instruction.
@@ -76,13 +89,13 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn or_(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_or::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts a bitwise logical exclusive or instruction.
@@ -93,13 +106,13 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn xor(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_xor::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts an addition instruction.
@@ -110,13 +123,13 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn add(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_int_add::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts a subtraction instruction.
@@ -127,13 +140,13 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn sub(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_int_sub::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts a multiplication instruction.
@@ -144,13 +157,13 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn mul(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_int_mul::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts a shift left instruction.
@@ -161,13 +174,13 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn shl(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_left_shift::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts a logical (zero fill) shift right instruction.
@@ -178,14 +191,14 @@ impl Builder {
     /// :rtype: Value
     #[pyo3(text_signature = "(self, lhs, rhs)")]
     fn lshr(&self, py: Python, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_right_shift::<IntValue>(
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             false,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts an integer comparison instruction.
@@ -196,16 +209,15 @@ impl Builder {
     /// :return: The boolean result.
     /// :rtype: Value
     #[pyo3(text_signature = "(self, pred, lhs, rhs)")]
-    #[allow(clippy::needless_pass_by_value)]
-    fn icmp(&self, py: Python, pred: IntPredicate, lhs: Value, rhs: Value) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, lhs.context(), rhs.context()])?;
+    fn icmp(&self, py: Python, pred: IntPredicate, lhs: &Value, rhs: &Value) -> PyResult<PyObject> {
+        let owner = Owner::merge(py, [&self.owner, lhs.owner(), rhs.owner()])?;
         let value = self.builder.build_int_compare::<IntValue>(
             pred.into(),
             unsafe { lhs.get() }.try_into()?,
             unsafe { rhs.get() }.try_into()?,
             "",
         );
-        unsafe { Value::from_any(py, self.context.clone(), value) }
+        unsafe { Value::from_any(py, owner, value) }
     }
 
     /// Inserts a call instruction.
@@ -215,37 +227,30 @@ impl Builder {
     /// :returns: The return value, or None if the function has a void return type.
     /// :rtype: Optional[Value]
     #[pyo3(text_signature = "(self, callee, args)")]
-    fn call(&self, py: Python, callee: &Value, args: &PySequence) -> PyResult<Option<PyObject>> {
-        context::require_same(
-            py,
-            values::extract_contexts(args.iter()?.filter_map(Result::ok))
-                .chain([self.context.clone(), callee.context().clone()]),
-        )?;
+    fn call(&self, py: Python, callee: &Value, args: Vec<Argument>) -> PyResult<Option<PyObject>> {
+        let arg_owners = args.iter().filter_map(Argument::owner);
+        let owner = Owner::merge(py, arg_owners.chain([&self.owner, callee.owner()]))?;
 
         let callable: Callable = unsafe { callee.get() }.try_into()?;
         let param_types = callable.ty.get_param_types();
-        if param_types.len() != args.len()? {
-            return Err(PyValueError::new_err(format!(
+        if param_types.len() != args.len() {
+            Err(PyValueError::new_err(format!(
                 "Expected {} arguments, got {}.",
                 param_types.len(),
-                args.len()?
-            )));
+                args.len()
+            )))?;
         }
 
         let args = args
-            .iter()?
+            .iter()
             .zip(param_types)
-            .map(|(v, t)| {
-                unsafe { values::extract_any(&t, v?) }?
-                    .try_into()
-                    .map_err(Into::into)
-            })
+            .map(|(arg, ty)| unsafe { arg.to_value(ty) }.map_err(Into::into))
             .collect::<PyResult<Vec<_>>>()?;
 
         let call = self.builder.build_call(callable.value, &args, "");
         let value = call.try_as_basic_value().left();
         value
-            .map(|v| unsafe { Value::from_any(py, callee.context().clone(), v) })
+            .map(|v| unsafe { Value::from_any(py, owner, v) })
             .transpose()
     }
 
@@ -268,7 +273,7 @@ impl Builder {
         r#true: Option<&PyAny>,
         r#false: Option<&PyAny>,
     ) -> PyResult<()> {
-        context::require_same(py, [&self.context, cond.context()])?;
+        Owner::merge(py, [&self.owner, cond.owner()])?;
         self.builder.try_build_if(
             unsafe { cond.get() }.try_into()?,
             || r#true.iter().try_for_each(|f| f.call0().map(|_| ())),
@@ -282,13 +287,12 @@ impl Builder {
     /// :returns: The branch instruction.
     /// :rtype: Instruction
     #[pyo3(text_signature = "(dest)")]
-    #[allow(clippy::needless_pass_by_value)]
     fn br(&self, py: Python, dest: PyRef<BasicBlock>) -> PyResult<PyObject> {
-        context::require_same(py, [&self.context, dest.as_ref().context()])?;
+        let owner = Owner::merge(py, [&self.owner, dest.as_ref().owner()])?;
         let inst = self
             .builder
             .build_unconditional_branch(unsafe { dest.get() });
-        unsafe { Value::from_any(py, self.context.clone(), inst) }
+        unsafe { Value::from_any(py, owner, inst) }
     }
 
     /// Inserts a return instruction.
@@ -298,15 +302,15 @@ impl Builder {
     /// :rtype: Instruction
     #[pyo3(text_signature = "(value)")]
     fn ret(&self, py: Python, value: Option<&Value>) -> PyResult<PyObject> {
-        let inst = match value {
-            None => self.builder.build_return(None),
+        let (inst, owner) = match value {
+            None => (self.builder.build_return(None), self.owner.clone_ref(py)),
             Some(value) => {
-                context::require_same(py, [&self.context, value.context()])?;
+                let owner = Owner::merge(py, [&self.owner, value.owner()])?;
                 let value = BasicValueEnum::try_from(unsafe { value.get() })?;
-                self.builder.build_return(Some(&value))
+                (self.builder.build_return(Some(&value)), owner)
             }
         };
-        unsafe { Value::from_any(py, self.context.clone(), inst) }
+        unsafe { Value::from_any(py, owner, inst) }
     }
 }
 
@@ -315,8 +319,8 @@ impl Builder {
         &self.builder
     }
 
-    pub(crate) fn context(&self) -> &Py<Context> {
-        &self.context
+    pub(crate) fn owner(&self) -> &Owner {
+        &self.owner
     }
 }
 
@@ -346,3 +350,28 @@ impl<'ctx> TryFrom<AnyValue<'ctx>> for Callable<'ctx> {
         .ok_or_else(|| PyValueError::new_err("Value is not callable."))
     }
 }
+
+#[derive(FromPyObject)]
+enum Argument<'py> {
+    Value(PyRef<'py, Value>),
+    Literal(Literal<'py>),
+}
+
+impl Argument<'_> {
+    fn owner(&self) -> Option<&Owner> {
+        match self {
+            Argument::Value(v) => Some(v.owner()),
+            Argument::Literal(_) => None,
+        }
+    }
+
+    unsafe fn to_value(&self, ty: BasicTypeEnum<'static>) -> PyResult<BasicMetadataValueEnum> {
+        match self {
+            Argument::Value(v) => v.get().try_into().map_err(Into::into),
+            Argument::Literal(l) => l
+                .to_value(types::basic_to_any(ty))?
+                .try_into()
+                .map_err(Into::into),
+        }
+    }
+}
diff --git a/pyqir/src/context.rs b/pyqir/src/context.rs
index e84496f7..8a8241ad 100644
--- a/pyqir/src/context.rs
+++ b/pyqir/src/context.rs
@@ -1,8 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-use pyo3::{exceptions::PyValueError, prelude::*, PyClass};
-use std::{borrow::Borrow, ops::Deref};
+use pyo3::prelude::*;
+use std::ops::Deref;
 
 #[pyclass]
 #[derive(Eq, PartialEq)]
@@ -24,30 +24,3 @@ impl Deref for Context {
         &self.0
     }
 }
-
-pub(crate) fn require_same(
-    py: Python,
-    contexts: impl IntoIterator<Item = impl Borrow<Py<Context>>>,
-) -> PyResult<()> {
-    // then_some is stabilized in Rust 1.62.
-    #[allow(clippy::unnecessary_lazy_evaluations)]
-    is_all_same(py, contexts)
-        .then(|| ())
-        .ok_or_else(|| PyValueError::new_err("Some objects come from a different context."))
-}
-
-fn is_all_same<T>(py: Python, items: impl IntoIterator<Item = impl Borrow<Py<T>>>) -> bool
-where
-    T: Eq + PyClass,
-{
-    let mut items = items.into_iter();
-    if let Some(mut prev) = items.next() {
-        for item in items {
-            if *item.borrow().borrow(py) != *prev.borrow().borrow(py) {
-                return false;
-            }
-            prev = item;
-        }
-    }
-    true
-}
diff --git a/pyqir/src/instructions.rs b/pyqir/src/instructions.rs
index 39bde8ee..9a71b313 100644
--- a/pyqir/src/instructions.rs
+++ b/pyqir/src/instructions.rs
@@ -3,10 +3,7 @@
 
 #![allow(clippy::used_underscore_binding)]
 
-use crate::{
-    context::Context,
-    values::{BasicBlock, Value},
-};
+use crate::values::{BasicBlock, Owner, Value};
 use either::Either::{Left, Right};
 use inkwell::{
     values::InstructionOpcode,
@@ -39,13 +36,11 @@ impl Instruction {
     /// :type: List[Value]
     #[getter]
     fn operands(slf: PyRef<Self>, py: Python) -> PyResult<Vec<PyObject>> {
-        let inst = slf.0;
-        let value = slf.into_super();
-        let context = value.context();
-        (0..inst.get_num_operands())
-            .map(|i| match inst.get_operand(i).unwrap() {
-                Left(value) => unsafe { Value::from_any(py, context.clone(), value) },
-                Right(block) => unsafe { Value::from_any(py, context.clone(), block) },
+        let owner = slf.as_ref().owner();
+        (0..slf.0.get_num_operands())
+            .map(|i| match slf.0.get_operand(i).unwrap() {
+                Left(value) => unsafe { Value::from_any(py, owner.clone_ref(py), value) },
+                Right(block) => unsafe { Value::from_any(py, owner.clone_ref(py), block) },
             })
             .collect()
     }
@@ -77,11 +72,11 @@ impl Instruction {
 impl Instruction {
     pub(crate) unsafe fn from_inst(
         py: Python,
-        context: Py<Context>,
+        owner: Owner,
         inst: InstructionValue,
     ) -> PyResult<PyObject> {
         let inst = transmute::<InstructionValue<'_>, InstructionValue<'static>>(inst);
-        let base = Value::init(context, inst.into()).add_subclass(Self(inst));
+        let base = Value::new(owner, inst.into()).add_subclass(Self(inst));
         match inst.get_opcode() {
             InstructionOpcode::Switch => Ok(Py::new(py, base.add_subclass(Switch))?.to_object(py)),
             InstructionOpcode::ICmp => Ok(Py::new(py, base.add_subclass(ICmp))?.to_object(py)),
@@ -322,8 +317,8 @@ impl Switch {
     fn cond(slf: PyRef<Self>, py: Python) -> PyResult<PyObject> {
         let inst = slf.into_super();
         let cond = inst.0.get_operand(0).unwrap().left().unwrap();
-        let context = inst.into_super().context().clone();
-        unsafe { Value::from_any(py, context, cond) }
+        let owner = inst.as_ref().owner().clone_ref(py);
+        unsafe { Value::from_any(py, owner, cond) }
     }
 
     /// The default successor block if none of the cases match.
@@ -333,8 +328,8 @@ impl Switch {
     fn default(slf: PyRef<Self>, py: Python) -> PyResult<PyObject> {
         let inst = slf.into_super();
         let block = inst.0.get_operand(1).unwrap().right().unwrap();
-        let context = inst.into_super().context().clone();
-        unsafe { Value::from_any(py, context, block) }
+        let owner = inst.as_ref().owner().clone_ref(py);
+        unsafe { Value::from_any(py, owner, block) }
     }
 
     /// The switch cases.
@@ -345,15 +340,15 @@ impl Switch {
         let inst_ref = slf.into_super();
         let inst = inst_ref.0;
         let value = inst_ref.into_super();
-        let context = value.context();
+        let owner = value.owner();
 
         (2..inst.get_num_operands())
             .step_by(2)
             .map(|i| {
                 let cond = inst.get_operand(i).unwrap().left().unwrap();
-                let cond = unsafe { Value::from_any(py, context.clone(), cond)? };
+                let cond = unsafe { Value::from_any(py, owner.clone_ref(py), cond)? };
                 let succ = inst.get_operand(i + 1).unwrap().right().unwrap();
-                let succ = unsafe { Value::from_any(py, context.clone(), succ)? };
+                let succ = unsafe { Value::from_any(py, owner.clone_ref(py), succ)? };
                 Ok((cond, succ))
             })
             .collect()
@@ -377,7 +372,7 @@ impl ICmp {
 
 /// An integer comparison predicate.
 #[pyclass]
-#[derive(Clone)]
+#[derive(Clone, Copy)]
 pub(crate) enum IntPredicate {
     #[pyo3(name = "EQ")]
     Eq,
@@ -452,7 +447,7 @@ impl FCmp {
 
 /// A floating-point comparison predicate.
 #[pyclass]
-#[derive(Clone)]
+#[derive(Clone, Copy)]
 pub(crate) enum FloatPredicate {
     #[pyo3(name = "FALSE")]
     False,
@@ -525,8 +520,8 @@ impl Call {
         let inst = slf.into_super();
         let last = inst.0.get_operand(inst.0.get_num_operands() - 1);
         let callee = last.unwrap().left().unwrap();
-        let context = inst.into_super().context().clone();
-        unsafe { Value::from_any(py, context, callee) }
+        let owner = inst.into_super().owner().clone_ref(py);
+        unsafe { Value::from_any(py, owner, callee) }
     }
 
     /// The arguments to the call.
@@ -554,13 +549,13 @@ impl Phi {
     fn incoming(slf: PyRef<Self>, py: Python) -> PyResult<Vec<(PyObject, PyObject)>> {
         let phi = slf.0;
         let value = slf.into_super().into_super();
-        let context = value.context();
+        let owner = value.owner();
 
         (0..phi.count_incoming())
             .map(|i| {
                 let (value, block) = phi.get_incoming(i).unwrap();
-                let value = unsafe { Value::from_any(py, context.clone(), value)? };
-                let block = unsafe { Value::from_any(py, context.clone(), block)? };
+                let value = unsafe { Value::from_any(py, owner.clone_ref(py), value)? };
+                let block = unsafe { Value::from_any(py, owner.clone_ref(py), block)? };
                 Ok((value, block))
             })
             .collect()
diff --git a/pyqir/src/lib.rs b/pyqir/src/lib.rs
index 512fb837..f05d9e42 100644
--- a/pyqir/src/lib.rs
+++ b/pyqir/src/lib.rs
@@ -1,15 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
-//
-// To store Inkwell objects in Python classes, we transmute the `'ctx` lifetime to static. You need
-// to be careful when using Inkwell types with unsafely extended lifetimes. Follow these rules:
-//
-// 1. When storing in a data type, always include a `Py<Context>` field containing the context
-//    originally referred to by `'ctx`.
-// 2. Before calling Inkwell methods that use `'ctx`, call `context::require_same` to assert that
-//    all contexts being used are the same.
 
 #![warn(clippy::all, clippy::pedantic)]
+#![allow(clippy::needless_pass_by_value)]
 
 #[cfg(feature = "llvm11-0")]
 extern crate llvm_sys_110 as llvm_sys;
diff --git a/pyqir/src/module.rs b/pyqir/src/module.rs
index ad3196cc..9a6ae675 100644
--- a/pyqir/src/module.rs
+++ b/pyqir/src/module.rs
@@ -1,6 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
+#![allow(clippy::used_underscore_binding)]
+
 use crate::{context::Context, values::Value};
 use inkwell::memory_buffer::MemoryBuffer;
 use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes};
@@ -37,20 +39,20 @@ impl Module {
     /// :rtype: Module
     /// :returns: The module.
     #[staticmethod]
-    #[pyo3(text_signature = "(ir, name=\"\")")]
-    fn from_ir(py: Python, ir: &str, name: Option<&str>) -> PyResult<Self> {
-        let context = Context::new();
+    #[pyo3(text_signature = "(context, ir, name=\"\")")]
+    fn from_ir(py: Python, context: Py<Context>, ir: &str, name: Option<&str>) -> PyResult<Self> {
         let buffer =
             MemoryBuffer::create_from_memory_range(ir.as_bytes(), name.unwrap_or_default());
-        let module = context
-            .create_module_from_ir(buffer)
-            .map_err(|e| PyValueError::new_err(e.to_string()))?;
-        Ok(Self {
-            module: unsafe {
+        let module = {
+            let context = context.borrow(py);
+            let module = context
+                .create_module_from_ir(buffer)
+                .map_err(|e| PyValueError::new_err(e.to_string()))?;
+            unsafe {
                 transmute::<inkwell::module::Module<'_>, inkwell::module::Module<'static>>(module)
-            },
-            context: Py::new(py, context)?,
-        })
+            }
+        };
+        Ok(Self { module, context })
     }
 
     /// Creates a module from LLVM bitcode.
@@ -60,18 +62,23 @@ impl Module {
     /// :rtype: Module
     /// :returns: The module.
     #[staticmethod]
-    #[pyo3(text_signature = "(bitcode, name=\"\")")]
-    fn from_bitcode(py: Python, bitcode: &[u8], name: Option<&str>) -> PyResult<Self> {
-        let context = Context::new();
+    #[pyo3(text_signature = "(context, bitcode, name=\"\")")]
+    fn from_bitcode(
+        py: Python,
+        context: Py<Context>,
+        bitcode: &[u8],
+        name: Option<&str>,
+    ) -> PyResult<Self> {
         let buffer = MemoryBuffer::create_from_memory_range(bitcode, name.unwrap_or_default());
-        let module = inkwell::module::Module::parse_bitcode_from_buffer(&buffer, &*context)
-            .map_err(|e| PyValueError::new_err(e.to_string()))?;
-        Ok(Self {
-            module: unsafe {
+        let module = {
+            let context = context.borrow(py);
+            let module = inkwell::module::Module::parse_bitcode_from_buffer(&buffer, &**context)
+                .map_err(|e| PyValueError::new_err(e.to_string()))?;
+            unsafe {
                 transmute::<inkwell::module::Module<'_>, inkwell::module::Module<'static>>(module)
-            },
-            context: Py::new(py, context)?,
-        })
+            }
+        };
+        Ok(Self { module, context })
     }
 
     /// The name of the original source file that this module was compiled from.
@@ -94,10 +101,11 @@ impl Module {
     ///
     /// :type: List[Function]
     #[getter]
-    fn functions(&self, py: Python) -> PyResult<Vec<PyObject>> {
-        self.module
+    fn functions(slf: Py<Module>, py: Python) -> PyResult<Vec<PyObject>> {
+        slf.borrow(py)
+            .module
             .get_functions()
-            .map(|f| unsafe { Value::from_any(py, self.context.clone(), f) })
+            .map(|f| unsafe { Value::from_any(py, slf.clone_ref(py).into(), f) })
             .collect()
     }
 
@@ -117,6 +125,14 @@ impl Module {
         &self.context
     }
 
+    /// Verifies that this module is valid.
+    ///
+    /// :returns: An error description if this module is invalid or `None` if this module is valid.
+    /// :rtype: Optional[str]
+    fn verify(&self) -> Option<String> {
+        self.module.verify().map_err(|e| e.to_string()).err()
+    }
+
     fn __str__(&self) -> String {
         self.module.to_string()
     }
@@ -128,6 +144,60 @@ impl Module {
     }
 }
 
+impl Eq for Module {}
+
+impl PartialEq for Module {
+    fn eq(&self, other: &Self) -> bool {
+        self.module == other.module
+    }
+}
+
+/// The linkage kind for a global value in a module.
+#[pyclass]
+#[derive(Clone, Copy)]
+pub(crate) enum Linkage {
+    #[pyo3(name = "APPENDING")]
+    Appending,
+    #[pyo3(name = "AVAILABLE_EXTERNALLY")]
+    AvailableExternally,
+    #[pyo3(name = "COMMON")]
+    Common,
+    #[pyo3(name = "EXTERNAL")]
+    External,
+    #[pyo3(name = "EXTERNAL_WEAK")]
+    ExternalWeak,
+    #[pyo3(name = "INTERNAL")]
+    Internal,
+    #[pyo3(name = "LINK_ONCE_ANY")]
+    LinkOnceAny,
+    #[pyo3(name = "LINK_ONCE_ODR")]
+    LinkOnceOdr,
+    #[pyo3(name = "PRIVATE")]
+    Private,
+    #[pyo3(name = "WEAK_ANY")]
+    WeakAny,
+    #[pyo3(name = "WEAK_ODR")]
+    WeakOdr,
+}
+
+impl From<Linkage> for inkwell::module::Linkage {
+    fn from(linkage: Linkage) -> Self {
+        match linkage {
+            Linkage::Appending => Self::Appending,
+            Linkage::AvailableExternally => Self::AvailableExternally,
+            Linkage::Common => Self::Common,
+            Linkage::External => Self::External,
+            Linkage::ExternalWeak => Self::ExternalWeak,
+            Linkage::Internal => Self::Internal,
+            Linkage::LinkOnceAny => Self::LinkOnceAny,
+            Linkage::LinkOnceOdr => Self::LinkOnceODR,
+            Linkage::Private => Self::Private,
+            Linkage::WeakAny => Self::WeakAny,
+            Linkage::WeakOdr => Self::WeakODR,
+        }
+    }
+}
+
 /// An attribute.
 #[pyclass(unsendable)]
 pub(crate) struct Attribute(pub(crate) inkwell::attributes::Attribute);
@@ -143,12 +213,3 @@ impl Attribute {
             .expect("Value is not valid UTF-8.")
     }
 }
-
-/// Verifies that a module is valid.
-///
-/// :returns: An error description if the module is invalid or `None` if the module is valid.
-/// :rtype: Optional[str]
-#[pyfunction]
-pub(crate) fn verify_module(module: &Module) -> Option<String> {
-    module.module.verify().map_err(|e| e.to_string()).err()
-}
diff --git a/pyqir/src/python.rs b/pyqir/src/python.rs
index 8a03e96a..9bdb3ee0 100644
--- a/pyqir/src/python.rs
+++ b/pyqir/src/python.rs
@@ -8,7 +8,7 @@ use crate::{
     instructions::{
         Call, FCmp, FloatPredicate, ICmp, Instruction, IntPredicate, Opcode, Phi, Switch,
     },
-    module::{verify_module, Attribute, Module},
+    module::{Attribute, Linkage, Module},
     qis::BasicQisBuilder,
     simple::SimpleModule,
     types::{
@@ -18,7 +18,7 @@ use crate::{
     values::{
         entry_point, extract_byte_string, global_byte_string, is_entry_point, is_interop_friendly,
         qubit, qubit_id, r#const, required_num_qubits, required_num_results, result, result_id,
-        BasicBlock, Constant, FloatConstant, Function, IntConstant, Linkage, Value,
+        BasicBlock, Constant, FloatConstant, Function, IntConstant, Value,
     },
 };
 use pyo3::prelude::*;
@@ -70,6 +70,5 @@ fn _native(_py: Python, m: &PyModule) -> PyResult<()> {
     m.add_function(wrap_pyfunction!(result_id, m)?)?;
     m.add_function(wrap_pyfunction!(result_type, m)?)?;
     m.add_function(wrap_pyfunction!(result, m)?)?;
-    m.add_function(wrap_pyfunction!(verify_module, m)?)?;
     Ok(())
 }
diff --git a/pyqir/src/qis.rs b/pyqir/src/qis.rs
index 35bc2c1f..97194b88 100644
--- a/pyqir/src/qis.rs
+++ b/pyqir/src/qis.rs
@@ -3,9 +3,9 @@
 
 use crate::{
     builder::Builder,
-    context,
-    values::{self, Value},
+    values::{ConvertError, Owner, Value},
 };
+use inkwell::values::FloatValue;
 use pyo3::prelude::*;
 use qirlib::qis::BuilderExt;
 use std::convert::TryInto;
@@ -34,7 +34,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, control, target)")]
     fn cx(&self, py: Python, control: &Value, target: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), control.context(), target.context()])?;
+        Owner::merge(py, [builder.owner(), control.owner(), target.owner()])?;
         unsafe { builder.get() }.build_cx(
             unsafe { control.get() }.try_into()?,
             unsafe { target.get() }.try_into()?,
@@ -50,7 +50,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, control, target)")]
     fn cz(&self, py: Python, control: &Value, target: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), control.context(), target.context()])?;
+        Owner::merge(py, [builder.owner(), control.owner(), target.owner()])?;
         unsafe { builder.get() }.build_cz(
             unsafe { control.get() }.try_into()?,
             unsafe { target.get() }.try_into()?,
@@ -65,7 +65,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn h(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_h(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -78,7 +78,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit, result)")]
     fn mz(&self, py: Python, qubit: &Value, result: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context(), result.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner(), result.owner()])?;
         unsafe { builder.get() }.build_mz(
             unsafe { qubit.get() }.try_into()?,
             unsafe { result.get() }.try_into()?,
@@ -93,7 +93,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn reset(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_reset(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -104,17 +104,23 @@ impl BasicQisBuilder {
     /// :param Value qubit: The qubit to rotate.
     /// :rtype: None
     #[pyo3(text_signature = "(self, theta, qubit)")]
-    fn rx(&self, py: Python, theta: &PyAny, qubit: &Value) -> PyResult<()> {
+    fn rx(&self, py: Python, theta: Angle, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(
+        Owner::merge(
             py,
-            values::extract_contexts([theta])
-                .chain([builder.context().clone(), qubit.context().clone()]),
+            [Some(builder.owner()), theta.owner(), Some(qubit.owner())]
+                .into_iter()
+                .flatten(),
         )?;
 
-        let context = builder.context().borrow(py);
-        let theta = unsafe { values::extract_any(&context.f64_type(), theta)? };
-        unsafe { builder.get() }.build_rx(theta.try_into()?, unsafe { qubit.get() }.try_into()?);
+        let context = builder.owner().context(py);
+        let context = context.borrow(py);
+        unsafe {
+            builder
+                .get()
+                .build_rx(theta.to_float(&context)?, qubit.get().try_into()?);
+        }
+
         Ok(())
     }
 
@@ -124,17 +130,23 @@ impl BasicQisBuilder {
     /// :param Value qubit: The qubit to rotate.
     /// :rtype: None
     #[pyo3(text_signature = "(self, theta, qubit)")]
-    fn ry(&self, py: Python, theta: &PyAny, qubit: &Value) -> PyResult<()> {
+    fn ry(&self, py: Python, theta: Angle, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(
+        Owner::merge(
             py,
-            values::extract_contexts([theta])
-                .chain([builder.context().clone(), qubit.context().clone()]),
+            [Some(builder.owner()), theta.owner(), Some(qubit.owner())]
+                .into_iter()
+                .flatten(),
         )?;
 
-        let context = builder.context().borrow(py);
-        let theta = unsafe { values::extract_any(&context.f64_type(), theta)? };
-        unsafe { builder.get() }.build_ry(theta.try_into()?, unsafe { qubit.get() }.try_into()?);
+        let context = builder.owner().context(py);
+        let context = context.borrow(py);
+        unsafe {
+            builder
+                .get()
+                .build_ry(theta.to_float(&context)?, qubit.get().try_into()?);
+        }
+
         Ok(())
     }
 
@@ -144,17 +156,23 @@ impl BasicQisBuilder {
     /// :param Value qubit: The qubit to rotate.
     /// :rtype: None
     #[pyo3(text_signature = "(self, theta, qubit)")]
-    fn rz(&self, py: Python, theta: &PyAny, qubit: &Value) -> PyResult<()> {
+    fn rz(&self, py: Python, theta: Angle, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(
+        Owner::merge(
             py,
-            values::extract_contexts([theta])
-                .chain([builder.context().clone(), qubit.context().clone()]),
+            [Some(builder.owner()), theta.owner(), Some(qubit.owner())]
+                .into_iter()
+                .flatten(),
         )?;
 
-        let context = builder.context().borrow(py);
-        let theta = unsafe { values::extract_any(&context.f64_type(), theta)? };
-        unsafe { builder.get() }.build_rz(theta.try_into()?, unsafe { qubit.get() }.try_into()?);
+        let context = builder.owner().context(py);
+        let context = context.borrow(py);
+        unsafe {
+            builder
+                .get()
+                .build_rz(theta.to_float(&context)?, qubit.get().try_into()?);
+        }
+
         Ok(())
     }
 
@@ -165,7 +183,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn s(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_s(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -177,7 +195,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn s_adj(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_s_adj(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -189,7 +207,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn t(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_t(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -201,7 +219,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn t_adj(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_t_adj(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -213,7 +231,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn x(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_x(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -225,7 +243,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn y(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_y(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -237,7 +255,7 @@ impl BasicQisBuilder {
     #[pyo3(text_signature = "(self, qubit)")]
     fn z(&self, py: Python, qubit: &Value) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), qubit.context()])?;
+        Owner::merge(py, [builder.owner(), qubit.owner()])?;
         unsafe { builder.get() }.build_z(unsafe { qubit.get() }.try_into()?);
         Ok(())
     }
@@ -263,7 +281,7 @@ impl BasicQisBuilder {
         zero: Option<&PyAny>,
     ) -> PyResult<()> {
         let builder = self.builder.borrow(py);
-        context::require_same(py, [builder.context(), cond.context()])?;
+        Owner::merge(py, [builder.owner(), cond.owner()])?;
         unsafe { builder.get() }.try_build_if_result(
             unsafe { cond.get() }.try_into()?,
             || one.iter().try_for_each(|f| f.call0().map(|_| ())),
@@ -271,3 +289,28 @@ impl BasicQisBuilder {
         )
     }
 }
+
+#[derive(FromPyObject)]
+enum Angle<'py> {
+    Value(PyRef<'py, Value>),
+    Constant(f64),
+}
+
+impl Angle<'_> {
+    fn owner(&self) -> Option<&Owner> {
+        match self {
+            Angle::Value(v) => Some(v.owner()),
+            Angle::Constant(_) => None,
+        }
+    }
+
+    unsafe fn to_float<'ctx>(
+        &self,
+        context: &'ctx inkwell::context::Context,
+    ) -> Result<FloatValue<'ctx>, ConvertError> {
+        match self {
+            Angle::Value(v) => v.get().try_into(),
+            &Angle::Constant(c) => Ok(context.f64_type().const_float(c)),
+        }
+    }
+}
diff --git a/pyqir/src/simple.rs b/pyqir/src/simple.rs
index edbd5b34..f7400acf 100644
--- a/pyqir/src/simple.rs
+++ b/pyqir/src/simple.rs
@@ -3,10 +3,10 @@
 
 use crate::{
     builder::Builder,
-    context::{self, Context},
+    context::Context,
     module::Module,
     types::FunctionType,
-    values::Value,
+    values::{Owner, Value},
 };
 use pyo3::{
     exceptions::{PyOSError, PyUnicodeDecodeError, PyValueError},
@@ -45,8 +45,8 @@ impl SimpleModule {
         context: Option<Py<Context>>,
     ) -> PyResult<SimpleModule> {
         let context = context.map_or_else(|| Py::new(py, Context::new()), Ok)?;
-        let module = Py::new(py, Module::new(py, context.clone(), name))?;
-        let builder = Py::new(py, Builder::new(py, context.clone()))?;
+        let module = Py::new(py, Module::new(py, context.clone_ref(py), name))?;
+        let builder = Py::new(py, Builder::new(py, context.clone_ref(py)))?;
 
         {
             let context = context.borrow(py);
@@ -68,35 +68,35 @@ impl SimpleModule {
 
     #[getter]
     fn context(&self, py: Python) -> Py<Context> {
-        self.module.borrow(py).context().clone()
+        self.module.borrow(py).context().clone_ref(py)
     }
 
     /// The global qubit register.
     ///
-    /// :type: Tuple[Value, ...]
+    /// :type: List[Value]
     #[getter]
     fn qubits(&self, py: Python) -> PyResult<Vec<PyObject>> {
         let module = self.module.borrow(py);
-        let context = module.context();
-        let context_ref = unsafe { module.get() }.get_context();
+        let context = unsafe { module.get() }.get_context();
         (0..self.num_qubits)
             .map(|id| unsafe {
-                Value::from_any(py, context.clone(), values::qubit(&context_ref, id))
+                let owner = Owner::Context(module.context().clone_ref(py));
+                Value::from_any(py, owner, values::qubit(&context, id))
             })
             .collect()
     }
 
     /// The global result register.
     ///
-    /// :type: Tuple[Value, ...]
+    /// :type: List[Value]
     #[getter]
     fn results(&self, py: Python) -> PyResult<Vec<PyObject>> {
         let module = self.module.borrow(py);
-        let context = module.context();
-        let context_ref = unsafe { module.get() }.get_context();
+        let context = unsafe { module.get() }.get_context();
         (0..self.num_results)
             .map(|id| unsafe {
-                Value::from_any(py, context.clone(), values::result(&context_ref, id))
+                let owner = Owner::Context(module.context().clone_ref(py));
+                Value::from_any(py, owner, values::result(&context, id))
             })
             .collect()
     }
@@ -105,8 +105,8 @@ impl SimpleModule {
     ///
     /// :type: Builder
     #[getter]
-    fn builder(&self) -> Py<Builder> {
-        self.builder.clone()
+    fn builder(&self) -> &Py<Builder> {
+        &self.builder
     }
 
     /// Emits the LLVM IR for the module as plain text.
@@ -141,9 +141,15 @@ impl SimpleModule {
         let module = self.module.borrow(py);
         let function_ty = unsafe { ty.get() };
         let ty = ty.into_super();
-        context::require_same(py, [module.context(), ty.context()])?;
+        let owner = Owner::merge(
+            py,
+            [
+                Owner::Module(self.module.clone_ref(py)),
+                Owner::Context(ty.context().clone_ref(py)),
+            ],
+        )?;
         let function = unsafe { module.get() }.add_function(name, function_ty, None);
-        unsafe { Value::from_any(py, ty.context().clone(), function) }
+        unsafe { Value::from_any(py, owner, function) }
     }
 
     /// Adds a global null-terminated byte string constant to the module.
@@ -155,7 +161,7 @@ impl SimpleModule {
     fn add_byte_string(&self, py: Python, value: &[u8]) -> PyResult<PyObject> {
         let module = self.module.borrow(py);
         let string = values::global_string(unsafe { module.get() }, value);
-        unsafe { Value::from_any(py, module.context().clone(), string) }
+        unsafe { Value::from_any(py, module.context().clone_ref(py).into(), string) }
     }
 }
 
diff --git a/pyqir/src/types.rs b/pyqir/src/types.rs
index 15d85872..1fa9271f 100644
--- a/pyqir/src/types.rs
+++ b/pyqir/src/types.rs
@@ -3,7 +3,7 @@
 
 #![allow(clippy::used_underscore_binding)]
 
-use crate::context::{self, Context};
+use crate::{context::Context, values::Owner};
 use inkwell::{
     types::{AnyType, AnyTypeEnum, BasicType, BasicTypeEnum},
     AddressSpace, LLVMReference,
@@ -15,7 +15,6 @@ use std::{convert::TryFrom, mem::transmute};
 
 /// A type.
 #[pyclass(subclass, unsendable)]
-#[derive(Clone)]
 pub(crate) struct Type {
     ty: AnyTypeEnum<'static>,
     context: Py<Context>,
@@ -149,17 +148,18 @@ impl IntType {
 /// :param Type ret: The return type.
 /// :param Sequence[Type] params: The parameter types.
 #[pyclass(extends = Type, unsendable)]
-#[derive(Clone)]
 pub(crate) struct FunctionType(inkwell::types::FunctionType<'static>);
 
 #[pymethods]
 impl FunctionType {
     #[new]
-    #[allow(clippy::needless_pass_by_value)]
-    fn new(py: Python, ret: &Type, params: Vec<Type>) -> PyResult<(Self, Type)> {
-        context::require_same(
+    fn new(py: Python, ret: &Type, params: Vec<PyRef<Type>>) -> PyResult<(Self, Type)> {
+        Owner::merge(
             py,
-            params.iter().map(|ty| &ty.context).chain([&ret.context]),
+            params
+                .iter()
+                .map(|ty| Owner::Context(ty.context.clone_ref(py)))
+                .chain([ret.context.clone_ref(py).into()]),
         )?;
 
         let ty = function(&ret.ty, params.iter().map(|ty| ty.ty))
@@ -169,7 +169,7 @@ impl FunctionType {
             Self(ty),
             Type {
                 ty: ty.into(),
-                context: ret.context.clone(),
+                context: ret.context.clone_ref(py),
             },
         ))
     }
@@ -180,7 +180,7 @@ impl FunctionType {
     #[getter]
     fn ret(slf: PyRef<Self>, py: Python) -> PyResult<PyObject> {
         let ret = slf.0.get_return_type();
-        let context = slf.into_super().context.clone();
+        let context = slf.into_super().context.clone_ref(py);
         match ret {
             None => Ok(Py::new(py, Type::void(py, context))?.to_object(py)),
             Some(ret) => unsafe { Type::from_any(py, context, basic_to_any(ret)) },
@@ -196,7 +196,7 @@ impl FunctionType {
         let context = &slf.into_super().context;
         params
             .into_iter()
-            .map(|ty| unsafe { Type::from_any(py, context.clone(), basic_to_any(ty)) })
+            .map(|ty| unsafe { Type::from_any(py, context.clone_ref(py), basic_to_any(ty)) })
             .collect()
     }
 }
@@ -230,7 +230,7 @@ impl StructType {
         let context = &slf.into_super().context;
         fields
             .into_iter()
-            .map(|ty| unsafe { Type::from_any(py, context.clone(), basic_to_any(ty)) })
+            .map(|ty| unsafe { Type::from_any(py, context.clone_ref(py), basic_to_any(ty)) })
             .collect()
     }
 }
@@ -247,7 +247,7 @@ impl ArrayType {
     #[getter]
     fn element(slf: PyRef<Self>, py: Python) -> PyResult<PyObject> {
         let ty = basic_to_any(slf.0.get_element_type());
-        let context = slf.into_super().context.clone();
+        let context = slf.into_super().context.clone_ref(py);
         unsafe { Type::from_any(py, context, ty) }
     }
 
@@ -269,7 +269,7 @@ pub(crate) struct PointerType(inkwell::types::PointerType<'static>);
 #[pymethods]
 impl PointerType {
     #[new]
-    fn new(pointee: &Type) -> PyResult<(Self, Type)> {
+    fn new(py: Python, pointee: &Type) -> PyResult<(Self, Type)> {
         let ty = match pointee.ty {
             AnyTypeEnum::ArrayType(a) => Ok(a.ptr_type(AddressSpace::Generic)),
             AnyTypeEnum::FloatType(f) => Ok(f.ptr_type(AddressSpace::Generic)),
@@ -285,7 +285,7 @@ impl PointerType {
             Self(ty),
             Type {
                 ty: ty.into(),
-                context: pointee.context.clone(),
+                context: pointee.context.clone_ref(py),
             },
         ))
     }
@@ -296,7 +296,7 @@ impl PointerType {
     #[getter]
     fn pointee(slf: PyRef<Self>, py: Python) -> PyResult<PyObject> {
         let ty = slf.0.get_element_type();
-        let context = slf.into_super().context.clone();
+        let context = slf.into_super().context.clone_ref(py);
         unsafe { Type::from_any(py, context, ty) }
     }
 
@@ -361,7 +361,7 @@ pub(crate) fn is_result_type(ty: &Type) -> bool {
     types::is_result(ty.ty)
 }
 
-fn basic_to_any(ty: BasicTypeEnum) -> AnyTypeEnum {
+pub(crate) fn basic_to_any(ty: BasicTypeEnum) -> AnyTypeEnum {
     match ty {
         BasicTypeEnum::ArrayType(a) => a.into(),
         BasicTypeEnum::FloatType(f) => f.into(),
diff --git a/pyqir/src/values.rs b/pyqir/src/values.rs
index fe6f052d..0428a714 100644
--- a/pyqir/src/values.rs
+++ b/pyqir/src/values.rs
@@ -4,14 +4,14 @@
 #![allow(clippy::used_underscore_binding)]
 
 use crate::{
-    context::{self, Context},
+    context::Context,
     instructions::Instruction,
-    module::{Attribute, Module},
+    module::{Attribute, Linkage, Module},
     types::{FunctionType, Type},
 };
 use inkwell::{
     attributes::AttributeLoc,
-    types::{AnyType, AnyTypeEnum},
+    types::AnyTypeEnum,
     values::{
         AnyValueEnum, BasicMetadataValueEnum, BasicValueEnum, FloatValue, FunctionValue,
         GlobalValue, InstructionValue, IntValue, PointerValue,
@@ -30,10 +30,12 @@ use pyo3::{
     conversion::ToPyObject,
     exceptions::{PyTypeError, PyValueError},
     prelude::*,
-    types::PyBytes,
+    types::{PyBytes, PyLong},
+    PyRef,
 };
 use qirlib::values;
 use std::{
+    borrow::Borrow,
     convert::{Into, TryFrom, TryInto},
     ffi::CStr,
     fmt::{self, Display, Formatter},
@@ -44,10 +46,9 @@ use std::{
 
 /// A value.
 #[pyclass(subclass, unsendable)]
-#[derive(Clone)]
 pub(crate) struct Value {
     value: AnyValue<'static>,
-    context: Py<Context>,
+    owner: Owner,
 }
 
 #[pymethods]
@@ -57,7 +58,7 @@ impl Value {
     /// :type: Type
     #[getter]
     fn r#type(&self, py: Python) -> PyResult<PyObject> {
-        unsafe { Type::from_any(py, self.context.clone(), self.value.ty()) }
+        unsafe { Type::from_any(py, self.owner.context(py), self.value.ty()) }
     }
 
     /// The name of this value or the empty string if this value is anonymous.
@@ -75,37 +76,118 @@ impl Value {
 }
 
 impl Value {
+    pub(crate) unsafe fn new(owner: Owner, value: AnyValue) -> PyClassInitializer<Self> {
+        let value = transmute::<AnyValue<'_>, AnyValue<'static>>(value);
+        PyClassInitializer::from(Self { value, owner })
+    }
+
     pub(crate) unsafe fn from_any<'ctx>(
         py: Python,
-        context: Py<Context>,
+        owner: Owner,
         value: impl Into<AnyValue<'ctx>>,
     ) -> PyResult<PyObject> {
         let value = transmute::<AnyValue<'_>, AnyValue<'static>>(value.into());
         #[allow(clippy::same_functions_in_if_condition)]
         if let Ok(inst) = value.try_into() {
-            Instruction::from_inst(py, context, inst)
+            Instruction::from_inst(py, owner, inst)
         } else if let Ok(block) = value.try_into() {
-            let base = PyClassInitializer::from(Self { value, context });
+            let base = PyClassInitializer::from(Self { value, owner });
             let block = base.add_subclass(BasicBlock(block));
             Ok(Py::new(py, block)?.to_object(py))
         } else if value.is_const() {
-            Constant::from_any(py, context, value)
+            Constant::from_any(py, owner, value)
         } else {
-            Ok(Py::new(py, Self { value, context })?.to_object(py))
+            Ok(Py::new(py, Self { value, owner })?.to_object(py))
         }
     }
 
-    pub(crate) unsafe fn init(context: Py<Context>, value: AnyValue) -> PyClassInitializer<Self> {
-        let value = transmute::<AnyValue<'_>, AnyValue<'static>>(value);
-        PyClassInitializer::from(Self { value, context })
-    }
-
     pub(crate) unsafe fn get(&self) -> AnyValue<'static> {
         self.value
     }
 
-    pub(crate) fn context(&self) -> &Py<Context> {
-        &self.context
+    pub(crate) fn owner(&self) -> &Owner {
+        &self.owner
+    }
+}
+
+/// To store Inkwell values in Python classes, we transmute the lifetime to `'static`. You need to
+/// be careful when using Inkwell types with unsafely extended lifetimes. Follow these rules:
+///
+/// 1. When storing in a data type, always include an `Owner` field containing the owning module, if
+///    there is one, or the context otherwise.
+/// 2. Before passing an LLVM object to an Inkwell function, call `Owner::merge` to ensure that the
+///    owners of all of the objects are compatible.
+pub(crate) enum Owner {
+    Context(Py<Context>),
+    Module(Py<Module>),
+}
+
+impl Owner {
+    pub(crate) fn context(&self, py: Python) -> Py<Context> {
+        match self {
+            Self::Context(context) => context.clone_ref(py),
+            Self::Module(module) => module.borrow(py).context().clone_ref(py),
+        }
+    }
+
+    /// Merges a sequence of owners into a single owner that lives at least as long as every owner
+    /// in the sequence.
+    ///
+    /// # Errors
+    /// Fails if the the given owners use more than one distinct context or module.
+    ///
+    /// # Panics
+    /// Panics if the sequence is empty.
+    pub(crate) fn merge(
+        py: Python,
+        owners: impl IntoIterator<Item = impl Borrow<Self>>,
+    ) -> PyResult<Self> {
+        owners
+            .into_iter()
+            .try_fold(None, |o1, o2| match (o1, o2.borrow()) {
+                (None, owner) => Ok(Some(owner.clone_ref(py))),
+                (Some(Self::Context(c1)), Self::Context(c2))
+                    if *c1.borrow(py) == *c2.borrow(py) =>
+                {
+                    Ok(Some(Self::Context(c1)))
+                }
+                (Some(Self::Context(c)), Self::Module(m))
+                    if *c.borrow(py) == *m.borrow(py).context().borrow(py) =>
+                {
+                    Ok(Some(Self::Module(m.clone_ref(py))))
+                }
+                (Some(Self::Module(m)), Self::Context(c))
+                    if *m.borrow(py).context().borrow(py) == *c.borrow(py) =>
+                {
+                    Ok(Some(Self::Module(m)))
+                }
+                (Some(Self::Module(m1)), Self::Module(m2)) if *m1.borrow(py) == *m2.borrow(py) => {
+                    Ok(Some(Self::Module(m1)))
+                }
+                _ => Err(PyValueError::new_err(
+                    "Some values are from different contexts or modules.",
+                )),
+            })
+            .map(|o| o.expect("No owners were given."))
+    }
+
+    pub(crate) fn clone_ref(&self, py: Python) -> Owner {
+        match self {
+            Self::Context(context) => Self::Context(context.clone_ref(py)),
+            Self::Module(module) => Self::Module(module.clone_ref(py)),
+        }
+    }
+}
+
+impl From<Py<Context>> for Owner {
+    fn from(context: Py<Context>) -> Self {
+        Self::Context(context)
+    }
+}
+
+impl From<Py<Module>> for Owner {
+    fn from(module: Py<Module>) -> Self {
+        Self::Module(module)
     }
 }
 
@@ -130,15 +212,28 @@ impl BasicBlock {
         py: Python,
         context: Py<Context>,
         name: &str,
-        parent: Option<&Function>,
-        before: Option<&BasicBlock>,
+        parent: Option<PyRef<Function>>,
+        before: Option<PyRef<BasicBlock>>,
     ) -> PyResult<PyClassInitializer<Self>> {
+        let parent_value = parent.as_ref().map(|f| f.0);
+        let parent = parent.map(PyRef::into_super);
+        let owner = Owner::merge(
+            py,
+            [
+                Some(&context.clone_ref(py).into()),
+                parent.as_ref().map(|f| &f.as_ref().owner),
+                before.as_ref().map(|b| &b.as_ref().owner),
+            ]
+            .into_iter()
+            .flatten(),
+        )?;
+
         let block = {
             let context = context.borrow(py);
-            let block = match (parent, before) {
+            let block = match (parent_value, before) {
                 (None, None) => Err(PyValueError::new_err("Can't create block without parent.")),
-                (Some(parent), None) => Ok(context.append_basic_block(parent.0, name)),
-                (Some(parent), Some(before)) if before.0.get_parent() != Some(parent.0) => Err(
+                (Some(parent), None) => Ok(context.append_basic_block(parent, name)),
+                (Some(parent), Some(before)) if before.0.get_parent() != Some(parent) => Err(
                     PyValueError::new_err("Insert before block isn't in parent function."),
                 ),
                 (_, Some(before)) => Ok(context.prepend_basic_block(before.0, name)),
@@ -153,7 +248,7 @@ impl BasicBlock {
         };
 
         let value = block.into();
-        Ok(PyClassInitializer::from(Value { value, context }).add_subclass(Self(block)))
+        Ok(PyClassInitializer::from(Value { value, owner }).add_subclass(Self(block)))
     }
 
     /// The instructions in this basic block.
@@ -161,16 +256,12 @@ impl BasicBlock {
     /// :type: List[Instruction]
     #[getter]
     fn instructions(slf: PyRef<Self>, py: Python) -> PyResult<Vec<PyObject>> {
-        let block = slf.0;
-        let context = &slf.into_super().context;
         let mut insts = Vec::new();
-        let mut inst = block.get_first_instruction();
-
+        let mut inst = slf.0.get_first_instruction();
         while let Some(i) = inst {
-            insts.push(unsafe { Instruction::from_inst(py, context.clone(), i) }?);
+            insts.push(unsafe { Instruction::from_inst(py, slf.as_ref().owner.clone_ref(py), i) }?);
             inst = i.get_next_instruction();
         }
-
         Ok(insts)
     }
 
@@ -181,8 +272,8 @@ impl BasicBlock {
     fn terminator(slf: PyRef<Self>, py: Python) -> PyResult<Option<PyObject>> {
         match slf.0.get_terminator() {
             Some(terminator) => {
-                let context = slf.into_super().context.clone();
-                unsafe { Instruction::from_inst(py, context, terminator) }.map(Some)
+                let owner = slf.into_super().owner.clone_ref(py);
+                unsafe { Instruction::from_inst(py, owner, terminator) }.map(Some)
             }
             None => Ok(None),
         }
@@ -220,7 +311,7 @@ impl Constant {
                 Err(PyValueError::new_err("Can't create null for this type."))
             }
         }?;
-        unsafe { Value::from_any(py, ty.context().clone(), value) }
+        unsafe { Value::from_any(py, ty.context().clone_ref(py).into(), value) }
     }
 
     /// Whether this value is the null value for its type.
@@ -233,10 +324,10 @@ impl Constant {
 }
 
 impl Constant {
-    unsafe fn from_any(py: Python, context: Py<Context>, value: AnyValue) -> PyResult<PyObject> {
+    unsafe fn from_any(py: Python, owner: Owner, value: AnyValue) -> PyResult<PyObject> {
         if value.is_const() {
             let value = transmute::<AnyValue<'_>, AnyValue<'static>>(value);
-            let base = PyClassInitializer::from(Value { value, context }).add_subclass(Constant);
+            let base = PyClassInitializer::from(Value { value, owner }).add_subclass(Constant);
             match value.try_into() {
                 Ok(AnyValueEnum::IntValue(_)) => {
                     Ok(Py::new(py, base.add_subclass(IntConstant))?.to_object(py))
@@ -304,16 +395,25 @@ impl Function {
         ty: PyRef<FunctionType>,
         linkage: Linkage,
         name: &str,
-        module: &Module,
+        module: Py<Module>,
     ) -> PyResult<PyClassInitializer<Self>> {
-        let function_ty = unsafe { ty.get() };
-        let context = module.context();
-        context::require_same(py, [ty.into_super().context(), context])?;
-        let function =
-            unsafe { module.get() }.add_function(name, function_ty, Some(linkage.into()));
-        Ok(unsafe { Value::init(context.clone(), function.into()) }
+        let owner = Owner::merge(
+            py,
+            [
+                Owner::Context(ty.as_ref().context().clone_ref(py)),
+                Owner::Module(module.clone_ref(py)),
+            ],
+        )?;
+
+        let value = unsafe { module.borrow(py).get() }.add_function(
+            name,
+            unsafe { ty.get() },
+            Some(linkage.into()),
+        );
+
+        Ok(unsafe { Value::new(owner, value.into()) }
             .add_subclass(Constant)
-            .add_subclass(Self(function)))
+            .add_subclass(Self(value)))
     }
 
     /// The parameters to this function.
@@ -322,10 +422,10 @@ impl Function {
     #[getter]
     fn params(slf: PyRef<Self>, py: Python) -> PyResult<Vec<PyObject>> {
         let params = slf.0.get_params();
-        let context = &slf.into_super().into_super().context;
+        let owner = &slf.into_super().into_super().owner;
         params
             .into_iter()
-            .map(|p| unsafe { Value::from_any(py, context.clone(), p) })
+            .map(|p| unsafe { Value::from_any(py, owner.clone_ref(py), p) })
             .collect()
     }
 
@@ -335,11 +435,11 @@ impl Function {
     #[getter]
     fn basic_blocks(slf: PyRef<Self>, py: Python) -> PyResult<Vec<PyObject>> {
         let function = slf.0;
-        let context = &slf.into_super().into_super().context;
+        let owner = &slf.into_super().into_super().owner;
         function
             .get_basic_blocks()
             .into_iter()
-            .map(|b| unsafe { Value::from_any(py, context.clone(), b) })
+            .map(|b| unsafe { Value::from_any(py, owner.clone_ref(py), b) })
             .collect()
     }
 
@@ -362,52 +462,6 @@ impl Function {
     }
 }
 
-/// The linkage kind for a global value in a module.
-#[pyclass]
-#[derive(Clone)]
-pub(crate) enum Linkage {
-    #[pyo3(name = "APPENDING")]
-    Appending,
-    #[pyo3(name = "AVAILABLE_EXTERNALLY")]
-    AvailableExternally,
-    #[pyo3(name = "COMMON")]
-    Common,
-    #[pyo3(name = "EXTERNAL")]
-    External,
-    #[pyo3(name = "EXTERNAL_WEAK")]
-    ExternalWeak,
-    #[pyo3(name = "INTERNAL")]
-    Internal,
-    #[pyo3(name = "LINK_ONCE_ANY")]
-    LinkOnceAny,
-    #[pyo3(name = "LINK_ONCE_ODR")]
-    LinkOnceOdr,
-    #[pyo3(name = "PRIVATE")]
-    Private,
-    #[pyo3(name = "WEAK_ANY")]
-    WeakAny,
-    #[pyo3(name = "WEAK_ODR")]
-    WeakOdr,
-}
-
-impl From<Linkage> for inkwell::module::Linkage {
-    fn from(linkage: Linkage) -> Self {
-        match linkage {
-            Linkage::Appending => Self::Appending,
-            Linkage::AvailableExternally => Self::AvailableExternally,
-            Linkage::Common => Self::Common,
-            Linkage::External => Self::External,
-            Linkage::ExternalWeak => Self::ExternalWeak,
-            Linkage::Internal => Self::Internal,
-            Linkage::LinkOnceAny => Self::LinkOnceAny,
-            Linkage::LinkOnceOdr => Self::LinkOnceODR,
-            Linkage::Private => Self::Private,
-            Linkage::WeakAny => Self::WeakAny,
-            Linkage::WeakOdr => Self::WeakODR,
-        }
-    }
-}
-
 #[derive(Clone, Copy)]
 pub(crate) enum AnyValue<'ctx> {
     Any(AnyValueEnum<'ctx>),
@@ -678,18 +732,39 @@ impl Drop for Message {
     }
 }
 
+#[derive(FromPyObject)]
+pub(crate) enum Literal<'py> {
+    Bool(bool),
+    Int(&'py PyLong),
+    Float(f64),
+}
+
+impl Literal<'_> {
+    pub(crate) fn to_value(&self, ty: AnyTypeEnum<'static>) -> PyResult<AnyValue> {
+        match (ty, self) {
+            (AnyTypeEnum::IntType(ty), &Self::Bool(b)) => Ok(ty.const_int(b.into(), false).into()),
+            (AnyTypeEnum::IntType(ty), &Self::Int(i)) => {
+                Ok(ty.const_int(i.extract()?, false).into())
+            }
+            (AnyTypeEnum::FloatType(ty), &Self::Float(f)) => Ok(ty.const_float(f).into()),
+            _ => Err(PyTypeError::new_err(
+                "Can't convert Python value into this type.",
+            )),
+        }
+    }
+}
+
 /// Creates a constant value.
 ///
 /// :param Type ty: The type of the value.
-/// :param Union[int, float] value: The value of the constant.
+/// :param Union[bool, int, float] value: The value of the constant.
 /// :returns: The constant value.
 /// :rtype: Value
 #[pyfunction]
 #[pyo3(text_signature = "(ty, value)")]
-pub(crate) fn r#const(py: Python, ty: &Type, value: &PyAny) -> PyResult<PyObject> {
-    let context = ty.context().clone();
-    let value = extract_constant(unsafe { &ty.get() }, value)?;
-    unsafe { Value::from_any(py, context, value) }
+pub(crate) fn r#const(py: Python, ty: &Type, value: Literal) -> PyResult<PyObject> {
+    let owner = ty.context().clone_ref(py).into();
+    unsafe { Value::from_any(py, owner, value.to_value(ty.get())?) }
 }
 
 /// Creates a static qubit value.
@@ -705,7 +780,7 @@ pub(crate) fn qubit(py: Python, context: Py<Context>, id: u64) -> PyResult<PyObj
         let value = values::qubit(&context.void_type().get_context(), id);
         unsafe { transmute::<PointerValue<'_>, PointerValue<'static>>(value) }
     };
-    unsafe { Value::from_any(py, context, value) }
+    unsafe { Value::from_any(py, Owner::Context(context), value) }
 }
 
 /// If the value is a static qubit ID, extracts it.
@@ -732,7 +807,7 @@ pub(crate) fn result(py: Python, context: Py<Context>, id: u64) -> PyResult<PyOb
         let value = values::result(&context.void_type().get_context(), id);
         unsafe { transmute::<PointerValue<'_>, PointerValue<'static>>(value) }
     };
-    unsafe { Value::from_any(py, context, value) }
+    unsafe { Value::from_any(py, Owner::Context(context), value) }
 }
 
 /// If the value is a static result ID, extracts it.
@@ -757,18 +832,18 @@ pub(crate) fn result_id(value: &Value) -> Option<u64> {
 #[pyfunction]
 pub(crate) fn entry_point(
     py: Python,
-    module: &Module,
+    module: Py<Module>,
     name: &str,
     required_num_qubits: u64,
     required_num_results: u64,
 ) -> PyResult<PyObject> {
     let entry_point = values::entry_point(
-        unsafe { module.get() },
+        unsafe { module.borrow(py).get() },
         name,
         required_num_qubits,
         required_num_results,
     );
-    unsafe { Value::from_any(py, module.context().clone(), entry_point) }
+    unsafe { Value::from_any(py, Owner::Module(module), entry_point) }
 }
 
 /// Whether the function is an entry point.
@@ -824,7 +899,7 @@ pub(crate) fn required_num_results(function: &Function) -> Option<u64> {
 #[pyo3(text_signature = "(module, value)")]
 pub(crate) fn global_byte_string(py: Python, module: &Module, value: &[u8]) -> PyResult<PyObject> {
     let string = values::global_string(unsafe { module.get() }, value);
-    unsafe { Value::from_any(py, module.context().clone(), string) }
+    unsafe { Value::from_any(py, module.context().clone_ref(py).into(), string) }
 }
 
 /// If the value is a pointer to a constant byte string, extracts it.
@@ -838,30 +913,3 @@ pub(crate) fn extract_byte_string<'p>(py: Python<'p>, value: &Value) -> Option<&
     let string = values::extract_string(unsafe { value.get() }.try_into().ok()?)?;
     Some(PyBytes::new(py, string))
 }
-
-pub(crate) unsafe fn extract_any<'ctx>(
-    ty: &impl AnyType<'ctx>,
-    ob: &PyAny,
-) -> PyResult<AnyValue<'ctx>> {
-    ob.extract()
-        .map(|v: Value| v.value)
-        .or_else(|_| extract_constant(ty, ob))
-}
-
-fn extract_constant<'ctx>(ty: &impl AnyType<'ctx>, ob: &PyAny) -> PyResult<AnyValue<'ctx>> {
-    match ty.as_any_type_enum() {
-        AnyTypeEnum::IntType(int) => Ok(int.const_int(ob.extract()?, true).into()),
-        AnyTypeEnum::FloatType(float) => Ok(float.const_float(ob.extract()?).into()),
-        _ => Err(PyTypeError::new_err(
-            "Can't convert Python value into this type.",
-        )),
-    }
-}
-
-pub(crate) fn extract_contexts<'a>(
-    values: impl IntoIterator<Item = &'a PyAny> + 'a,
-) -> impl Iterator<Item = Py<Context>> + 'a {
-    values
-        .into_iter()
-        .filter_map(|v| Some(v.extract::<Value>().ok()?.context))
-}


Test patch:
diff --git a/pyqir/tests/test_conversions.py b/pyqir/tests/test_conversions.py
index a94ac72f..1ca53c21 100644
--- a/pyqir/tests/test_conversions.py
+++ b/pyqir/tests/test_conversions.py
@@ -1,7 +1,7 @@
 # Copyright (c) Microsoft Corporation.
 # Licensed under the MIT License.
 
-from pyqir import BasicQisBuilder, SimpleModule, Module
+from pyqir import BasicQisBuilder, Context, SimpleModule, Module
 
 
 def get_module() -> SimpleModule:
@@ -13,31 +13,31 @@ def get_module() -> SimpleModule:
 
 def test_ir_round_trip_is_identical() -> None:
     expected_ir = get_module().ir()
-    bitcode = Module.from_ir(expected_ir, "test").bitcode
-    converted_ir = str(Module.from_bitcode(bitcode, "test"))
+    bitcode = Module.from_ir(Context(), expected_ir, "test").bitcode
+    converted_ir = str(Module.from_bitcode(Context(), bitcode, "test"))
     assert expected_ir == converted_ir
 
 
 def test_ir_round_trip_is_not_identical_when_module_name_is_not_supplied() -> None:
     expected_ir = get_module().ir()
-    bitcode = Module.from_ir(expected_ir).bitcode
-    converted_ir = str(Module.from_bitcode(bitcode))
+    bitcode = Module.from_ir(Context(), expected_ir).bitcode
+    converted_ir = str(Module.from_bitcode(Context(), bitcode))
     assert expected_ir != converted_ir
 
 
 def test_module_name_persists_in_conversion() -> None:
     expected_ir = get_module().ir()
-    bitcode = Module.from_ir(expected_ir, "test").bitcode
-    converted_ir = str(Module.from_bitcode(bitcode, "test2"))
+    bitcode = Module.from_ir(Context(), expected_ir, "test").bitcode
+    converted_ir = str(Module.from_bitcode(Context(), bitcode, "test2"))
     assert expected_ir != converted_ir
     assert "; ModuleID = 'test2'" in converted_ir
 
 
 def test_file_name_persists_in_conversion() -> None:
     expected_ir = get_module().ir()
-    module1 = Module.from_ir(expected_ir, "test")
+    module1 = Module.from_ir(Context(), expected_ir, "test")
     module1.source_filename = "some file"
-    module2 = Module.from_bitcode(module1.bitcode, "test")
+    module2 = Module.from_bitcode(Context(), module1.bitcode, "test")
     module2.source_filename = "some other file"
     converted_ir = str(module2)
     assert expected_ir != converted_ir
@@ -46,14 +46,14 @@ def test_file_name_persists_in_conversion() -> None:
 
 def test_ir_to_bitcode_returns_bytes_type() -> None:
     expected_ir = get_module().ir()
-    bitcode = Module.from_ir(expected_ir, "test").bitcode
+    bitcode = Module.from_ir(Context(), expected_ir, "test").bitcode
     assert isinstance(bitcode, bytes)
 
 
 def test_bitcode_to_ir_returns_str_type() -> None:
     expected_ir = get_module().ir()
-    bitcode = Module.from_ir(expected_ir, "test").bitcode
-    converted_ir = str(Module.from_bitcode(bitcode, "test"))
+    bitcode = Module.from_ir(Context(), expected_ir, "test").bitcode
+    converted_ir = str(Module.from_bitcode(Context(), bitcode, "test"))
     assert isinstance(converted_ir, str)
 
 
diff --git a/pyqir/tests/test_entry_points.py b/pyqir/tests/test_entry_points.py
index 4a753e2d..6aa852bb 100644
--- a/pyqir/tests/test_entry_points.py
+++ b/pyqir/tests/test_entry_points.py
@@ -52,7 +52,7 @@ def test_multiple() -> None:
     module = Module(Context(), "test")
     bell_function = _bell(module)
     external_function = _external(module)
-    assert pyqir.verify_module(module) is None
+    assert module.verify() is None
 
     names = list(map(lambda f: f.name, filter(pyqir.is_entry_point, module.functions)))
     assert len(names) == 2
@@ -64,7 +64,7 @@ def test_duplicate_name() -> None:
     module = Module(Context(), "test")
     bell1 = _bell(module)
     bell2 = _bell(module)
-    assert pyqir.verify_module(module) is None
+    assert module.verify() is None
     assert bell1.name != bell2.name
 
     names = list(map(lambda f: f.name, filter(pyqir.is_entry_point, module.functions)))
@@ -79,7 +79,7 @@ def test_invalid_module() -> None:
     main = pyqir.entry_point(module, "main", 0, 0)
     BasicBlock(context, "", main)
     assert (
-        pyqir.verify_module(module)
+        module.verify()
         == "Basic Block in function 'main' does not have terminator!\nlabel %0\n"
     )
 
@@ -96,7 +96,7 @@ def test_append_blocks() -> None:
     builder.insert_at_end(exit)
     builder.ret(None)
 
-    assert pyqir.verify_module(module) is None
+    assert module.verify() is None
     assert list(map(lambda b: b.name, main.basic_blocks)) == ["entry", "exit"]
 
 
@@ -115,7 +115,7 @@ def test_prepend_block(with_parent: bool) -> None:
     )
     builder.br(exit)
 
-    assert pyqir.verify_module(module) is None
+    assert module.verify() is None
     assert list(map(lambda b: b.name, main.basic_blocks)) == ["entry", "exit"]
 
 
diff --git a/pyqir/tests/test_generator.py b/pyqir/tests/test_generator.py
index d00fbff2..a4295470 100644
--- a/pyqir/tests/test_generator.py
+++ b/pyqir/tests/test_generator.py
@@ -7,10 +7,25 @@
 about the generated IR.
 """
 
+from pathlib import Path
+
 import pytest
 
 import pyqir
-from pyqir import BasicQisBuilder, Context, FunctionType, IntType, SimpleModule
+from pyqir import (
+    BasicBlock,
+    BasicQisBuilder,
+    Builder,
+    Context,
+    Function,
+    FunctionType,
+    Instruction,
+    IntType,
+    Linkage,
+    Module,
+    SimpleModule,
+    Type,
+)
 
 
 def test_bell() -> None:
@@ -139,7 +154,7 @@ def test_multiple_contexts() -> None:
     m1 = SimpleModule("m1", 0, 0)
     m2 = SimpleModule("m2", 0, 0)
     with pytest.raises(
-        ValueError, match=r"^Some objects come from a different context\.$"
+        ValueError, match=r"^Some values are from different contexts or modules\.$"
     ):
         m1.add_external_function(
             "f",
@@ -189,3 +204,45 @@ def test_shared_context() -> None:
     qis2.mz(m2.qubits[0], m2.results[0])
 
     assert m1.ir() == m2.ir()
+
+
+def test_function_lifetime() -> None:
+    def make_func() -> Function:
+        c = Context()
+        m = Module(c, "test")
+        return Function(FunctionType(Type.void(c), []), Linkage.EXTERNAL, "f", m)
+
+    assert str(make_func()) == "declare void @f()\n"
+
+
+def test_instruction_lifetime() -> None:
+    def make_inst() -> Instruction:
+        m = SimpleModule("test", 1, 1)
+        i8 = IntType(m.context, 8)
+        f = m.add_external_function("f", FunctionType(i8, []))
+        x = m.builder.call(f, [])
+        assert x is not None
+        v = m.builder.add(x, pyqir.const(i8, 2))
+        assert isinstance(v, Instruction)
+        return v
+
+    assert str(make_inst()) == "  %1 = add i8 %0, 2"
+
+
+def test_parsed_function_lifetime() -> None:
+    def get_entry() -> Function:
+        m = Module.from_bitcode(Context(), Path("tests/hello.bc").read_bytes())
+        return next(filter(pyqir.is_entry_point, m.functions))
+
+    assert get_entry().name == "program__main"
+
+
+def test_builder_lifetime() -> None:
+    def make_builder() -> Builder:
+        c = Context()
+        m = Module(c, "test")
+        b = Builder(c)
+        b.insert_at_end(BasicBlock(c, "", pyqir.entry_point(m, "main", 0, 0)))
+        return b
+
+    assert str(make_builder().ret(None)) == "  ret void"
diff --git a/pyqir/tests/test_parser.py b/pyqir/tests/test_parser.py
index a2f92fdb..b9abf33a 100644
--- a/pyqir/tests/test_parser.py
+++ b/pyqir/tests/test_parser.py
@@ -9,6 +9,7 @@
     BasicBlock,
     Call,
     Constant,
+    Context,
     Function,
     IntConstant,
     IntType,
@@ -26,7 +27,7 @@
 
 def test_parser() -> None:
     bitcode = Path("tests/teleportchain.baseprofile.bc").read_bytes()
-    mod = Module.from_bitcode(bitcode)
+    mod = Module.from_bitcode(Context(), bitcode)
     assert len(mod.functions) == 21
 
     func_name = (
@@ -90,7 +91,7 @@ def test_parser() -> None:
 
 def test_parser_select_support() -> None:
     bitcode = Path("tests/select.bc").read_bytes()
-    mod = Module.from_bitcode(bitcode)
+    mod = Module.from_bitcode(Context(), bitcode)
     func = next(filter(is_entry_point, mod.functions))
     block = func.basic_blocks[0]
     select = block.instructions[5]
@@ -121,7 +122,7 @@ def test_parser_select_support() -> None:
 
 def test_global_string() -> None:
     bitcode = Path("tests/hello.bc").read_bytes()
-    mod = Module.from_bitcode(bitcode)
+    mod = Module.from_bitcode(Context(), bitcode)
     func_name = "program__main__body"
     func = next(filter(lambda f: f.name == func_name, mod.functions))
     assert isinstance(func, Function)
@@ -139,7 +140,7 @@ def test_global_string() -> None:
 
 def test_parser_zext_support() -> None:
     bitcode = Path("tests/select.bc").read_bytes()
-    mod = Module.from_bitcode(bitcode)
+    mod = Module.from_bitcode(Context(), bitcode)
     func = next(filter(is_entry_point, mod.functions))
     block = func.basic_blocks[0]
     inst = block.instructions[7]
@@ -162,13 +163,13 @@ def test_parser_zext_support() -> None:
 def test_loading_invalid_bitcode() -> None:
     bitcode = Path("tests/teleportchain.ll.reference").read_bytes()
     with pytest.raises(ValueError) as e:
-        Module.from_bitcode(bitcode)
+        Module.from_bitcode(Context(), bitcode)
     assert e.value.args[0] == "Invalid bitcode signature"
 
 
 def test_parser_internals() -> None:
     bitcode = Path("tests/teleportchain.baseprofile.bc").read_bytes()
-    mod = Module.from_bitcode(bitcode)
+    mod = Module.from_bitcode(Context(), bitcode)
     func_name = (
         "TeleportChain__DemonstrateTeleportationUsingPresharedEntanglement__Interop"
     )