Merge #267

267: Add the `CommandReturn` type. r=hudson-ayers a=jrvanwhy

The `command` system call will return a `CommandReturn`. `CommandReturn` is practically free to construct and contains methods that allow drivers to interpret the register values it contains.

Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
diff --git a/core/platform/src/command_return.rs b/core/platform/src/command_return.rs
new file mode 100644
index 0000000..7fa7302
--- /dev/null
+++ b/core/platform/src/command_return.rs
@@ -0,0 +1,163 @@
+use crate::{return_variant, ErrorCode, ReturnVariant};
+
+/// The response type from `command`. Can represent a successful value or a
+/// failure.
+#[derive(Clone, Copy)]
+pub struct CommandReturn {
+    pub(crate) return_variant: ReturnVariant,
+    // r1, r2, and r3 should only contain 32-bit values. However, these are
+    // converted directly from usizes returned by RawSyscalls::four_arg_syscall.
+    // To avoid casting twice (both when converting to a Command Return and when
+    // calling a get_*() function), we store the usizes directly. Then using the
+    // CommandReturn only involves one conversion for each of r1, r2, and r3,
+    // performed in the get_*() functions.
+    pub(crate) r1: usize,
+    pub(crate) r2: usize,
+    pub(crate) r3: usize,
+}
+
+impl CommandReturn {
+    // I generally expect CommandReturn to be used with pattern matching, e.g.:
+    //
+    //     let command_return = Syscalls::command(314, 1, 1, 2);
+    //     if let Some((val1, val2)) = command_return.get_success_2_u32() {
+    //         // ...
+    //     } else if let Some(error_code) = command_return.get_failure() {
+    //         // ...
+    //     } else {
+    //         // Incorrect return variant
+    //     }
+
+    /// Returns true if this CommandReturn is of type Failure. Note that this
+    /// does not return true for other failure types, such as Failure with u32.
+    pub fn is_failure(&self) -> bool {
+        self.return_variant == return_variant::FAILURE
+    }
+
+    /// Returns true if this CommandReturn is of type Failure with u32.
+    pub fn is_failure_u32(&self) -> bool {
+        self.return_variant == return_variant::FAILURE_U32
+    }
+
+    /// Returns true if this CommandReturn is of type Failure with 2 u32.
+    pub fn is_failure_2_u32(&self) -> bool {
+        self.return_variant == return_variant::FAILURE_2_U32
+    }
+
+    /// Returns true if this CommandReturn is of type Failure with u64.
+    pub fn is_failure_u64(&self) -> bool {
+        self.return_variant == return_variant::FAILURE_U64
+    }
+
+    /// Returns true if this CommandReturn is of type Success. Note that this
+    /// does not return true for other success types, such as Success with u32.
+    pub fn is_success(&self) -> bool {
+        self.return_variant == return_variant::SUCCESS
+    }
+
+    /// Returns true if this CommandReturn is of type Success with u32.
+    pub fn is_success_u32(&self) -> bool {
+        self.return_variant == return_variant::SUCCESS_U32
+    }
+
+    /// Returns true if this CommandReturn is of type Success with 2 u32.
+    pub fn is_success_2_u32(&self) -> bool {
+        self.return_variant == return_variant::SUCCESS_2_U32
+    }
+
+    /// Returns true if this CommandReturn is of type Success with u64.
+    pub fn is_success_u64(&self) -> bool {
+        self.return_variant == return_variant::SUCCESS_U64
+    }
+
+    /// Returns true if this CommandReturn is of type Success with 3 u32.
+    pub fn is_success_3_u32(&self) -> bool {
+        self.return_variant == return_variant::SUCCESS_3_U32
+    }
+
+    /// Returns true if this CommandReturn is of type Success with u32 and u64.
+    pub fn is_success_u32_u64(&self) -> bool {
+        self.return_variant == return_variant::SUCCESS_U32_U64
+    }
+
+    /// Returns the error code if this CommandReturn is of type Failure.
+    pub fn get_failure(&self) -> Option<ErrorCode> {
+        if !self.is_failure() {
+            return None;
+        }
+        Some(self.r1.into())
+    }
+
+    /// Returns the error code and value if this CommandReturn is of type
+    /// Failure with u32.
+    pub fn get_failure_u32(&self) -> Option<(ErrorCode, u32)> {
+        if !self.is_failure_u32() {
+            return None;
+        }
+        Some((self.r1.into(), self.r2 as u32))
+    }
+
+    /// Returns the error code and return values if this CommandReturn is of
+    /// type Failure with 2 u32.
+    pub fn get_failure_2_u32(&self) -> Option<(ErrorCode, u32, u32)> {
+        if !self.is_failure_2_u32() {
+            return None;
+        }
+        Some((self.r1.into(), self.r2 as u32, self.r3 as u32))
+    }
+
+    /// Returns the error code and return value if this CommandReturn is of type
+    /// Failure with u64.
+    pub fn get_failure_u64(&self) -> Option<(ErrorCode, u64)> {
+        if !self.is_failure_u64() {
+            return None;
+        }
+        Some((self.r1.into(), self.r2 as u64 + ((self.r3 as u64) << 32)))
+    }
+
+    /// Returns the value if this CommandReturn is of type Success with u32.
+    pub fn get_success_u32(&self) -> Option<u32> {
+        if !self.is_success_u32() {
+            return None;
+        }
+        Some(self.r1 as u32)
+    }
+
+    /// Returns the values if this CommandReturn is of type Success with 2 u32.
+    pub fn get_success_2_u32(&self) -> Option<(u32, u32)> {
+        if !self.is_success_2_u32() {
+            return None;
+        }
+        Some((self.r1 as u32, self.r2 as u32))
+    }
+
+    /// Returns the value if this CommandReturn is of type Success with u64.
+    pub fn get_success_u64(&self) -> Option<u64> {
+        if !self.is_success_u64() {
+            return None;
+        }
+        Some(self.r1 as u64 + ((self.r2 as u64) << 32))
+    }
+
+    /// Returns the values if this CommandReturn is of type Success with 3 u32.
+    pub fn get_success_3_u32(&self) -> Option<(u32, u32, u32)> {
+        if !self.is_success_3_u32() {
+            return None;
+        }
+        Some((self.r1 as u32, self.r2 as u32, self.r3 as u32))
+    }
+
+    /// Returns the values if this CommandReturn is of type Success with u32 and
+    /// u64.
+    pub fn get_success_u32_u64(&self) -> Option<(u32, u64)> {
+        if !self.is_success_u32_u64() {
+            return None;
+        }
+        Some((self.r1 as u32, self.r2 as u64 + ((self.r3 as u64) << 32)))
+    }
+
+    /// Returns the return variant of this command.
+    pub fn return_variant(&self) -> ReturnVariant {
+        self.return_variant
+    }
+}
diff --git a/core/platform/src/command_return_tests.rs b/core/platform/src/command_return_tests.rs
new file mode 100644
index 0000000..1712f71
--- /dev/null
+++ b/core/platform/src/command_return_tests.rs
@@ -0,0 +1,325 @@
+use crate::{error_code, return_variant, CommandReturn};
+
+#[test]
+fn failure() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::FAILURE,
+        r1: error_code::RESERVE.into(),
+        r2: 1002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), true);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), Some(error_code::RESERVE));
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(command_return.return_variant(), return_variant::FAILURE);
+}
+
+#[test]
+fn failure_u32() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::FAILURE_U32,
+        r1: error_code::OFF.into(),
+        r2: 1002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), true);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(
+        command_return.get_failure_u32(),
+        Some((error_code::OFF, 1002))
+    );
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(command_return.return_variant(), return_variant::FAILURE_U32);
+}
+
+#[test]
+fn failure_2_u32() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::FAILURE_2_U32,
+        r1: error_code::ALREADY.into(),
+        r2: 1002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), true);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(
+        command_return.get_failure_2_u32(),
+        Some((error_code::ALREADY, 1002, 1003))
+    );
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(
+        command_return.return_variant(),
+        return_variant::FAILURE_2_U32
+    );
+}
+
+#[test]
+fn failure_u64() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::FAILURE_U64,
+        r1: error_code::BUSY.into(),
+        r2: 0x00001002,
+        r3: 0x00001003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), true);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(
+        command_return.get_failure_u64(),
+        Some((error_code::BUSY, 0x00001003_00001002))
+    );
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(command_return.return_variant(), return_variant::FAILURE_U64);
+}
+
+#[test]
+fn success() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::SUCCESS,
+        r1: 1001,
+        r2: 1002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), true);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(command_return.return_variant(), return_variant::SUCCESS);
+}
+
+#[test]
+fn success_u32() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::SUCCESS_U32,
+        r1: 1001,
+        r2: 1002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), true);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), Some(1001));
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(command_return.return_variant(), return_variant::SUCCESS_U32);
+}
+
+#[test]
+fn success_2_u32() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::SUCCESS_2_U32,
+        r1: 1001,
+        r2: 1002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), true);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), Some((1001, 1002)));
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(
+        command_return.return_variant(),
+        return_variant::SUCCESS_2_U32
+    );
+}
+
+#[test]
+fn success_u64() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::SUCCESS_U64,
+        r1: 0x00001001,
+        r2: 0x00001002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), true);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), Some(0x00001002_00001001));
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(command_return.return_variant(), return_variant::SUCCESS_U64);
+}
+
+#[test]
+fn success_3_u32() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::SUCCESS_3_U32,
+        r1: 1001,
+        r2: 1002,
+        r3: 1003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), true);
+    assert_eq!(command_return.is_success_u32_u64(), false);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), Some((1001, 1002, 1003)));
+    assert_eq!(command_return.get_success_u32_u64(), None);
+    assert_eq!(
+        command_return.return_variant(),
+        return_variant::SUCCESS_3_U32
+    );
+}
+
+#[test]
+fn success_u32_u64() {
+    let command_return = CommandReturn {
+        return_variant: return_variant::SUCCESS_U32_U64,
+        r1: 1001,
+        r2: 0x00001002,
+        r3: 0x00001003,
+    };
+    assert_eq!(command_return.is_failure(), false);
+    assert_eq!(command_return.is_failure_u32(), false);
+    assert_eq!(command_return.is_failure_2_u32(), false);
+    assert_eq!(command_return.is_failure_u64(), false);
+    assert_eq!(command_return.is_success(), false);
+    assert_eq!(command_return.is_success_u32(), false);
+    assert_eq!(command_return.is_success_2_u32(), false);
+    assert_eq!(command_return.is_success_u64(), false);
+    assert_eq!(command_return.is_success_3_u32(), false);
+    assert_eq!(command_return.is_success_u32_u64(), true);
+    assert_eq!(command_return.get_failure(), None);
+    assert_eq!(command_return.get_failure_u32(), None);
+    assert_eq!(command_return.get_failure_2_u32(), None);
+    assert_eq!(command_return.get_failure_u64(), None);
+    assert_eq!(command_return.get_success_u32(), None);
+    assert_eq!(command_return.get_success_2_u32(), None);
+    assert_eq!(command_return.get_success_u64(), None);
+    assert_eq!(command_return.get_success_3_u32(), None);
+    assert_eq!(
+        command_return.get_success_u32_u64(),
+        Some((1001, 0x00001003_00001002))
+    );
+    assert_eq!(
+        command_return.return_variant(),
+        return_variant::SUCCESS_U32_U64
+    );
+}
diff --git a/core/platform/src/error_code.rs b/core/platform/src/error_code.rs
index bd083e0..63cc34e 100644
--- a/core/platform/src/error_code.rs
+++ b/core/platform/src/error_code.rs
@@ -3,6 +3,11 @@
 /// response to a system call.
 // ErrorCode is not an enum so that conversion from the kernel's return value (a
 // `usize` in a register) is free.
+// TODO: derive(Debug) is currently only enabled for test builds, which is
+// necessary so it can be used in assert_eq!. We should develop a lighter-weight
+// Debug implementation and see if it is small enough to enable on non-Debug
+// builds.
+#[cfg_attr(test, derive(Debug))]
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub struct ErrorCode(usize);
 
diff --git a/core/platform/src/lib.rs b/core/platform/src/lib.rs
index 9343145..45021ca 100644
--- a/core/platform/src/lib.rs
+++ b/core/platform/src/lib.rs
@@ -1,14 +1,19 @@
 #![no_std]
 
 mod async_traits;
+mod command_return;
 pub mod error_code;
 mod raw_syscalls;
-pub mod return_type;
+pub mod return_variant;
 mod syscalls;
 mod syscalls_impl;
 
 pub use async_traits::{CallbackContext, FreeCallback, Locator, MethodCallback};
+pub use command_return::CommandReturn;
 pub use error_code::ErrorCode;
 pub use raw_syscalls::{OneArgMemop, RawSyscalls, YieldType, ZeroArgMemop};
-pub use return_type::ReturnType;
+pub use return_variant::ReturnVariant;
 pub use syscalls::Syscalls;
+
+#[cfg(test)]
+mod command_return_tests;
diff --git a/core/platform/src/return_type.rs b/core/platform/src/return_type.rs
deleted file mode 100644
index da19e87..0000000
--- a/core/platform/src/return_type.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-/// `ReturnType` describes what value type the kernel has returned.
-// ReturnType is not an enum so that it can be converted from a u32 for free.
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub struct ReturnType(u32);
-
-impl From<u32> for ReturnType {
-    fn from(value: u32) -> ReturnType {
-        ReturnType(value)
-    }
-}
-
-impl From<ReturnType> for u32 {
-    fn from(return_type: ReturnType) -> u32 {
-        return_type.0
-    }
-}
-
-pub const FAILURE: ReturnType = ReturnType(0);
-pub const FAILURE_U32: ReturnType = ReturnType(1);
-pub const FAILURE_2_U32: ReturnType = ReturnType(2);
-pub const FAILURE_U64: ReturnType = ReturnType(3);
-pub const SUCCESS: ReturnType = ReturnType(128);
-pub const SUCCESS_U32: ReturnType = ReturnType(129);
-pub const SUCCESS_2_U32: ReturnType = ReturnType(130);
-pub const SUCCESS_U64: ReturnType = ReturnType(131);
-pub const SUCCESS_3_U32: ReturnType = ReturnType(132);
-pub const SUCCESS_U32_U64: ReturnType = ReturnType(133);
diff --git a/core/platform/src/return_variant.rs b/core/platform/src/return_variant.rs
new file mode 100644
index 0000000..e0d6fbf
--- /dev/null
+++ b/core/platform/src/return_variant.rs
@@ -0,0 +1,32 @@
+/// `ReturnVariant` describes what value type the kernel has returned.
+// ReturnVariant is not an enum so that it can be converted from a u32 for free.
+// TODO: derive(Debug) is currently only enabled for test builds, which is
+// necessary so it can be used in assert_eq!. We should develop a lighter-weight
+// Debug implementation and see if it is small enough to enable on non-Debug
+// builds.
+#[cfg_attr(test, derive(Debug))]
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct ReturnVariant(u32);
+
+impl From<u32> for ReturnVariant {
+    fn from(value: u32) -> ReturnVariant {
+        ReturnVariant(value)
+    }
+}
+
+impl From<ReturnVariant> for u32 {
+    fn from(return_variant: ReturnVariant) -> u32 {
+        return_variant.0
+    }
+}
+
+pub const FAILURE: ReturnVariant = ReturnVariant(0);
+pub const FAILURE_U32: ReturnVariant = ReturnVariant(1);
+pub const FAILURE_2_U32: ReturnVariant = ReturnVariant(2);
+pub const FAILURE_U64: ReturnVariant = ReturnVariant(3);
+pub const SUCCESS: ReturnVariant = ReturnVariant(128);
+pub const SUCCESS_U32: ReturnVariant = ReturnVariant(129);
+pub const SUCCESS_2_U32: ReturnVariant = ReturnVariant(130);
+pub const SUCCESS_U64: ReturnVariant = ReturnVariant(131);
+pub const SUCCESS_3_U32: ReturnVariant = ReturnVariant(132);
+pub const SUCCESS_U32_U64: ReturnVariant = ReturnVariant(133);
diff --git a/core/platform/src/syscalls_impl.rs b/core/platform/src/syscalls_impl.rs
index 8f24467..506ad64 100644
--- a/core/platform/src/syscalls_impl.rs
+++ b/core/platform/src/syscalls_impl.rs
@@ -1,6 +1,6 @@
 //! Implements `Syscalls` for all types that implement `RawSyscalls`.
 
-use crate::{return_type, RawSyscalls, Syscalls, YieldType};
+use crate::{return_variant, RawSyscalls, Syscalls, YieldType};
 
 impl<S: RawSyscalls> Syscalls for S {
     // -------------------------------------------------------------------------
@@ -12,6 +12,6 @@
     }
 
     fn yield_no_wait() -> bool {
-        Self::raw_yield(YieldType::NoWait) != return_type::FAILURE.into()
+        Self::raw_yield(YieldType::NoWait) != return_variant::FAILURE.into()
     }
 }