Skip to content

Commit 1dc5e72

Browse files
committed
Initial API commit, mostly copied from DuckASM
DuckASM was my internal API for code-generation in my JIT (DuckLogic). The good thing is is that this project is open source! The bad news is that the rest of DuckLogic isn't (yet). This was the basic API I was using for static reflection, with a little more documentation added on I'll copy the implementation over soon
0 parents  commit 1dc5e72

File tree

8 files changed

+957
-0
lines changed

8 files changed

+957
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
Cargo.lock

Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "static-reflect"
3+
version = "0.1.0"
4+
edition = "2018"
5+
description = "Static type information, giving a form of compile-time reflection"
6+
license = "MIT"
7+
8+
[dependencies]
9+
# Optional: Support for zerogc
10+
zerogc = { version = "0.1.3", optional = true }
11+
# Optional: Support for numeric traits
12+
num-traits = { version = "0.2.14", optional = true }
13+
14+
[features]
15+
default = ["never", "builtins"]
16+
# Support using the never type
17+
never = []
18+
# Support the 'builtin' alternative to stdlib types
19+
builtins = []
20+
21+
22+
[workspace]

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Techcable (Nicholas Schlabach)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
static-reflect-rust
2+
====================
3+
Compile-time reflection in rust!
4+
5+
The original use case is type-checking generated code in a JIT compiler (with zero runtime cost).
6+
However, other use cases are certainly possible :)
7+
8+
Contributions are welcome!
9+
I'd be happy to add more features as long as they align with the general philosophy
10+
of compile-time reflection.
11+
12+
Unfortunately, there is very little documentation right now.
13+
Hopefully that will change in the future.

src/builtins.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//! Types builtin to the 'static reflection' system
2+
//!
3+
//! These are mostly FFI-safe alternatives to the standard library
4+
//! types.
5+
use std::mem::MaybeUninit;
6+
use crate::StaticReflect;
7+
8+
/// A FFi-safe slice type (`&[T]`)
9+
///
10+
/// Unlike the rust type, this has a well-defined C representation.
11+
///
12+
/// Internally, this is just a pointer and a length.
13+
///
14+
/// ## Safety
15+
/// This type maintains no in variants on its internal data.
16+
///
17+
/// However, since it is meant to be used with FFI and in other
18+
/// unsafe situations, this is often fine.
19+
/// The plus side is you can transmute to/from `[usize; 2]`
20+
/// without fear.
21+
#[derive(Debug)]
22+
#[repr(C)]
23+
pub struct AsmSlice<T> {
24+
/// A pointer to the start of the memory
25+
///
26+
/// May never be null, unless
27+
pub ptr: *mut T,
28+
/// The length of the slice
29+
pub len: usize
30+
}
31+
/// A clone implementation that blindly
32+
/// copies the underlying bytes.
33+
impl<T: StaticReflect> Clone for AsmSlice<T> {
34+
#[inline]
35+
fn clone(&self) -> Self {
36+
*self
37+
}
38+
}
39+
impl<T: StaticReflect> Copy for AsmSlice<T> {}
40+
41+
42+
/// A FFI-safe UTF8 string.
43+
///
44+
/// Unlike the rust type, this has a well-defined C representation.
45+
///
46+
/// ## Safety
47+
/// The underlying is expected to be UTF8. However,
48+
/// like its [AsmSlice] counterpart, all fields are public
49+
/// and this type does not maintain any invariants.
50+
#[repr(C)]
51+
#[derive(Copy, Clone, Debug)]
52+
pub struct AsmStr {
53+
/// The underlying memory of the string
54+
pub bytes: AsmSlice<u8>
55+
}
56+
impl AsmStr {
57+
/// A pointer to the bytes of the string
58+
#[inline]
59+
pub fn bytes_ptr(&self) -> *mut u8 {
60+
self.bytes.ptr
61+
}
62+
/// The length of the string in bytes
63+
#[inline]
64+
pub fn len(&self) -> usize {
65+
self.bytes.len
66+
}
67+
}
68+
69+
/// A FFI-safe alternative to Rust's [std::optional::Option].
70+
///
71+
/// Unlike the Rust type, this does not use the null-pointer
72+
/// optimization.
73+
///
74+
/// NOTE: This type doesn't implement Drop.
75+
///
76+
/// ## Safety
77+
/// This type does not enforce its safety variants,
78+
/// just like [AsmSlice]. However, the first field must be
79+
/// a valid `bool`.
80+
///
81+
/// A valid type can only be in one of two states:
82+
/// 1. `{present: false, value: undefined}`
83+
/// 2. `{present: false, value: any}`
84+
#[derive(Debug)]
85+
#[repr(C)]
86+
pub struct AsmOption<T> {
87+
present: bool,
88+
value: MaybeUninit<T>
89+
}
90+
impl<T> AsmOption<T> {
91+
/// An option with no value
92+
#[inline]
93+
pub fn none() -> AsmOption<T> {
94+
AsmOption {
95+
present: false,
96+
value: MaybeUninit::uninit()
97+
}
98+
}
99+
/// An option with a value
100+
#[inline]
101+
pub fn some(value: T) -> AsmOption<T> {
102+
AsmOption {
103+
present: true,
104+
value: MaybeUninit::new(value)
105+
}
106+
}
107+
/// Assume that this option is valid
108+
///
109+
/// Technically, it should already be invalid
110+
/// to have undefined internals.
111+
/// However, this is still unsafe as a sort of lint.
112+
#[inline]
113+
pub unsafe fn assume_valid(self) -> Option<T> {
114+
if self.present {
115+
Some(self.value.assume_init())
116+
} else {
117+
None
118+
}
119+
}
120+
/// If the value of the option is present.
121+
#[inline]
122+
pub fn is_present(&self) -> bool {
123+
self.present
124+
}
125+
}

src/core.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use crate::StaticReflect;
2+
use crate::types::{TypeInfo, IntSize};
3+
4+
macro_rules! impl_primitive {
5+
($target:ty => $info:expr) => {
6+
unsafe impl StaticReflect for $target {
7+
const TYPE_INFO: &'static TypeInfo<'static> = &$info;
8+
}
9+
}
10+
}
11+
macro_rules! impl_ints {
12+
($($target:ty),*) => {
13+
$(unsafe impl StaticReflect for $target {
14+
#[allow(unused_comparisons)]
15+
const TYPE_INFO: &'static TypeInfo<'static> = &TypeInfo::Integer {
16+
size: {
17+
let size = std::mem::size_of::<$target>();
18+
match IntSize::from_bytes(size) {
19+
Ok(s) => s,
20+
Err(_) => panic!("Invalid size")
21+
}
22+
},
23+
signed: <$target>::MIN < 0,
24+
};
25+
})*
26+
}
27+
}
28+
// NOTE: Pointer sized integers have machine-dependent implementation :(
29+
impl_ints!(u8, u16, u32, u64, i8, i16, i32, i64, usize, isize);
30+
31+
#[cfg(feature = "builtins")]
32+
impl_primitive!(str => TypeInfo::Str);
33+
impl_primitive!(() => TypeInfo::Unit);
34+
unsafe impl <T: StaticReflect> StaticReflect for *mut T {
35+
const TYPE_INFO: &'static TypeInfo<'static> = &TypeInfo::Pointer;
36+
}
37+
unsafe impl <T: StaticReflect> StaticReflect for *const T {
38+
const TYPE_INFO: &'static TypeInfo<'static> = &TypeInfo::Pointer;
39+
}

src/lib.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! A system for getting static type information.
2+
//!
3+
//! This effectively gives (some) of the power of reflection
4+
//! without any of the runtime cost!
5+
//!
6+
//! The original use case was type-checking generated code in a JIT compiler (with zero runtime cost).
7+
//! However, other use cases are certainly possible :)
8+
//!
9+
//! Contributions are welcome!
10+
//! I'd be happy to add more features as long as they align with the general philosophy
11+
//! of compile-time reflection.
12+
#![deny(missing_docs)]
13+
#![feature(
14+
const_fn, // We rely on const eval :(
15+
const_panic, const_option, // We use Option::unwrap
16+
)]
17+
#![cfg_attr(feature = "never", feature(never_type))]
18+
19+
#[cfg(feature = "builtins")]
20+
pub mod builtins;
21+
pub mod types;
22+
23+
mod core;
24+
25+
use crate::types::TypeInfo;
26+
27+
/// The trait for types whose information can be accessed via static reflection.
28+
///
29+
/// In order to proper access any fields,
30+
/// the representation must be C-compatible.
31+
/// Otherwise, transmutes and pointer-arithmetic are
32+
/// pointless because they are already undefined behavior.
33+
///
34+
/// ## Safety
35+
/// Incorrect implementation of this trait is considered
36+
/// undefined behavior. All the static type information must
37+
/// be correct at runtime.
38+
///
39+
/// For example, if this type gives field information via [FieldReflect],
40+
/// then the field information **must** match the representation
41+
/// at runtime.
42+
///
43+
/// The advantage of this is that other crates can rely
44+
/// on the representation being stable (for example, JIT compilers can use it).
45+
///
46+
/// The type must be `#[repr(C)]` or have some other
47+
/// form of FFI safety.
48+
pub unsafe trait StaticReflect {
49+
/// The static information about the type's representation
50+
const TYPE_INFO: &'static TypeInfo<'static>;
51+
}
52+
53+
/// A type that supports accessing its fields via reflection.
54+
///
55+
/// All fields are assumed to be defined in a way that is compatible
56+
/// with the the C ABI. In other words, the type must be `#[repr(C)]`
57+
///
58+
/// ## Safety
59+
/// Implementing this type incorrectly is undefined behavior.
60+
pub unsafe trait FieldReflect: StaticReflect {
61+
/// A magic structure that can be used to access
62+
/// field info by name
63+
type NamedFieldInfo;
64+
/// Static information on this structure's fields,
65+
/// where each field's information is given by name
66+
const NAMED_FIELD_INFO: Self::NamedFieldInfo;
67+
}

0 commit comments

Comments
 (0)