add procedural macro typeflags

This commit is contained in:
Jianfeng Jiang 2022-10-19 11:34:47 +08:00
parent 344fba6a3d
commit dd1e21b97c
14 changed files with 802 additions and 0 deletions

38
src/Cargo.lock generated
View File

@ -50,12 +50,27 @@ dependencies = [
"spin 0.7.1",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "font8x8"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "json"
version = "0.12.4"
@ -117,6 +132,15 @@ dependencies = [
"spin 0.9.4",
]
[[package]]
name = "kxos-rights-proc"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "kxos-std"
version = "0.1.0"
@ -131,6 +155,20 @@ dependencies = [
"xmas-elf",
]
[[package]]
name = "kxos-typeflags"
version = "0.1.0"
dependencies = [
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "kxos-typeflags-util"
version = "0.1.0"
[[package]]
name = "kxos-util"
version = "0.1.0"

View File

@ -18,6 +18,9 @@ members = [
"kxos-virtio",
"kxos-util",
"kxos-frame-pod-derive",
"kxos-typeflags",
"kxos-typeflags-util",
"kxos-rights-proc",
]
[package.metadata.bootloader]

View File

@ -0,0 +1,8 @@
[package]
name = "kxos-typeflags-util"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -0,0 +1,21 @@
//! define macro assert_type_same
use crate::same::SameAs;
pub type AssertTypeSame<Lhs, Rhs> = <Lhs as SameAs<Rhs>>::Output;
#[macro_export]
macro_rules! assert_type_same {
($lhs:ty, $rhs:ty) => {
const _: $crate::assert::AssertTypeSame<$lhs, $rhs> = crate::True;
};
}
#[cfg(test)]
mod test {
#[test]
fn test() {
assert_type_same!(u16, u16);
assert_type_same!((), ());
}
}

View File

@ -0,0 +1,78 @@
//! Type level bools
pub use std::ops::BitAnd as And;
pub use std::ops::BitOr as Or;
pub use std::ops::Not;
pub trait Bool {}
/// Type-level "true".
pub struct True;
/// Type-level "false".
pub struct False;
impl Bool for True {}
impl Bool for False {}
impl Not for True {
type Output = False;
fn not(self) -> Self::Output {
unimplemented!("not supposed to be used")
}
}
impl Not for False {
type Output = True;
fn not(self) -> Self::Output {
unimplemented!("not supposed to be used")
}
}
impl<B: Bool> And<B> for True {
type Output = B;
fn bitand(self, _rhs: B) -> Self::Output {
unimplemented!("not supposed to be used")
}
}
impl<B: Bool> And<B> for False {
type Output = False;
fn bitand(self, _rhs: B) -> Self::Output {
unimplemented!("not supposed to be used")
}
}
impl<B: Bool> Or<B> for True {
type Output = True;
fn bitor(self, _rhs: B) -> Self::Output {
unimplemented!("not supposed to be used")
}
}
impl<B: Bool> Or<B> for False {
type Output = B;
fn bitor(self, _rhs: B) -> Self::Output {
unimplemented!("not supposed to be used")
}
}
pub type NotOp<B> = <B as Not>::Output;
pub type AndOp<B0, B1> = <B0 as And<B1>>::Output;
pub type OrOp<B0, B1> = <B0 as Or<B1>>::Output;
// In where clause, we can only use trait bounds, but not equal bounds.
// For certain situation, we need to do some comparison in where clause.
// e.g., we need to determine the result type of `SetContainOp` is `True` or `False`.
// Since Sometype == True is not allowed, We can use SomeType: IsTrue to determine the result.
pub trait IsTrue {}
pub trait IsFalse {}
impl IsTrue for True {}
impl IsFalse for False {}

View File

@ -0,0 +1,29 @@
//! Type Level If
use crate::bool::{False, True};
pub trait If<B1, B2> {
type Output;
}
impl<B1, B2> If<B1, B2> for True {
type Output = B1;
}
impl<B1, B2> If<B1, B2> for False {
type Output = B2;
}
pub type IfOp<Cond, B1, B2> = <Cond as If<B1, B2>>::Output;
#[cfg(test)]
mod test {
use super::*;
use crate::*;
#[test]
fn test() {
assert_type_same!(IfOp<True, u32, ()>, u32);
assert_type_same!(IfOp<False, (), usize>, usize);
}
}

View File

@ -0,0 +1,15 @@
//! The content of this crate is from another project CapComp.
//! This crate defines common type level operations, like SameAsOp, and Bool type operations.
//! Besides, this crate defines operations to deal with type sets, like SetContain and SetInclude.
//! When use kxos-typeflags or kxos-rights-poc, this crate should also be added as a dependency.
pub mod assert;
pub mod bool;
pub mod if_;
pub mod same;
pub mod set;
pub use crate::bool::{And, AndOp, False, Not, NotOp, Or, OrOp, True, IsFalse, IsTrue};
pub use crate::same::{SameAs, SameAsOp};
pub use crate::set::{Cons, Nil, Set, SetContain, SetContainOp, SetInclude, SetIncludeOp};
pub use assert::AssertTypeSame;

View File

@ -0,0 +1,28 @@
//! Traits used to check if two types are the same, returning a Bool.
//! This check happens at compile time
use crate::bool::{Bool, False, True};
pub trait SameAs<T> {
type Output: Bool;
}
// A type is always same as itself
impl<T> SameAs<T> for T {
type Output = True;
}
impl SameAs<False> for True {
type Output = False;
}
impl SameAs<True> for False {
type Output = False;
}
// How to implement self reflection?
// impl <T, U> SameAs<T> for U where T: SameAs<U>, {
// type Output = <U as SameAs<T>>::Output;
// }
pub type SameAsOp<T, U> = <T as SameAs<U>>::Output;

View File

@ -0,0 +1,107 @@
//! Common types and traits to deal with type-level sets
use std::marker::PhantomData;
use crate::{
False, OrOp, True,
SameAs, SameAsOp,
And, AndOp,
if_::{If, IfOp},
};
use std::ops::BitOr as Or;
/// A marker trait for type-level sets.
pub trait Set {}
/// An non-empty type-level set.
pub struct Cons<T, S: Set>(PhantomData<(T, S)>);
impl<T, S: Set> Cons<T, S> {
pub fn new() -> Self {
Cons(PhantomData)
}
}
/// An empty type-level set.
pub struct Nil;
impl<T, S: Set> Set for Cons<T, S> {}
impl Set for Nil {}
/// A trait operator to check if `T` is a member of a type set;
pub trait SetContain<T> {
type Output;
}
pub type SetContainOp<Set, Item> = <Set as SetContain<Item>>::Output;
impl<T> SetContain<T> for Nil {
type Output = False;
}
impl<T, U, S> SetContain<T> for Cons<U, S>
where
S: Set + SetContain<T>,
U: SameAs<T>,
SameAsOp<U, T>: Or<SetContainOp<S, T>>,
{
type Output = OrOp<SameAsOp<U, T>, SetContainOp<S, T>>;
}
/// A trait operator to check if a set A includes a set B, i.e., A is a superset of B.
pub trait SetInclude<S: Set> {
type Output;
}
pub type SetIncludeOp<Super, Sub> = <Super as SetInclude<Sub>>::Output;
impl SetInclude<Nil> for Nil {
type Output = True;
}
impl<T, S: Set> SetInclude<Cons<T, S>> for Nil {
type Output = False;
}
impl<T, S: Set> SetInclude<Nil> for Cons<T, S> {
type Output = True;
}
impl<SuperT, SuperS, SubT, SubS> SetInclude<Cons<SubT, SubS>> for Cons<SuperT, SuperS>
where
SubS: Set,
SuperS: Set + SetInclude<SubS> + SetContain<SubT>,
SuperT: SameAs<SubT>,
SetContainOp<SuperS, SubT>: And<SetIncludeOp<SuperS, SubS>>,
SameAsOp<SuperT, SubT>: If<
SetIncludeOp<SuperS, SubS>,
AndOp<SetContainOp<SuperS, SubT>, SetIncludeOp<SuperS, SubS>>,
>,
{
type Output = IfOp<
SameAsOp<SuperT, SubT>,
SetIncludeOp<SuperS, SubS>,
AndOp<SetContainOp<SuperS, SubT>, SetIncludeOp<SuperS, SubS>>,
>;
}
#[cfg(test)]
mod test {
use crate::*;
#[test]
fn test() {
assert_type_same!(SetContainOp<Cons<True, Nil>, False>, False);
assert_type_same!(SetContainOp<Cons<True, Nil>, True>, True);
assert_type_same!(
SetIncludeOp<Cons<True, Cons<True, Nil>>, Cons<True, Nil>>,
True
);
assert_type_same!(
SetIncludeOp<Cons<True, Cons<True, Nil>>, Cons<False, Nil>>,
False
);
}
}

View File

@ -0,0 +1,15 @@
[package]
name = "kxos-typeflags"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = {version = "1.0.90"}
itertools = "0.10.5"

View File

@ -0,0 +1,163 @@
use itertools::Itertools;
use proc_macro2::{Ident, TokenStream};
use quote::{quote, TokenStreamExt};
use syn::Expr;
use crate::type_flag::TypeFlagDef;
const EMPTY_SET_NAME: &'static str = "::kxos_typeflags_util::Nil";
const SET_NAME: &'static str = "::kxos_typeflags_util::Cons";
/// A flagSet represent the combination of differnt flag item.
/// e.g. [Read, Write], [Read], [] are all flag sets.
/// The order of flagItem does not matters. So flag sets with same sets of items should be viewed as the same set.
pub struct FlagSet {
items: Vec<FlagItem>,
}
impl FlagSet {
/// create a new empty flag set
pub fn new() -> Self {
FlagSet { items: Vec::new() }
}
/// add a flag item
pub fn push_item(&mut self, flag_item: FlagItem) {
self.items.push(flag_item);
}
/// the flag set string. debug use.
pub fn to_string(&self) -> String {
if self.items.len() == 0 {
return EMPTY_SET_NAME.to_string();
}
let mut res = EMPTY_SET_NAME.to_string();
for item in self.items.iter() {
let replace_set = format!(
"{}<{}, {}>",
SET_NAME,
item.ident.to_string(),
EMPTY_SET_NAME
);
res = res.replace(EMPTY_SET_NAME, &replace_set);
}
res
}
/// the tokens represents the flag set type name
pub fn type_name_tokens(&self) -> TokenStream {
let mut res = quote!(::kxos_typeflags_util::Nil);
for item in self.items.iter() {
let ident = item.ident.clone();
// insert new item as head
let new_res = quote! {
::kxos_typeflags_util::Cons<#ident, #res>
};
res = new_res;
}
res
}
/// the number of items
pub fn len(&self) -> usize {
self.items.len()
}
/// the tokens to impl main trait for the current flagset
pub fn impl_main_trait_tokens(&self, type_flags_def: &TypeFlagDef) -> TokenStream {
let trait_ident = type_flags_def.trait_ident();
let name = self.type_name_tokens();
let mut all_tokens = quote! (
impl #trait_ident for #name
);
all_tokens.append_all(self.inner_tokens(type_flags_def));
all_tokens
}
/// the impl main trait inner content
fn inner_tokens(&self, type_flags_def: &TypeFlagDef) -> TokenStream {
let ty = type_flags_def.val_type();
let item_vals = self.items.iter().map(|item| item.val.clone());
// quote seems unable to use string for types.
// So we hardcode all types here.
if item_vals.len() == 0 {
quote!(
{
const BITS: #ty = 0 ;
fn new() -> Self {
::kxos_typeflags_util::Nil
}
}
)
} else {
quote!(
{
const BITS: #ty = #(#item_vals)|* ;
fn new() -> Self {
::kxos_typeflags_util::Cons::new()
}
}
)
}
}
/// The token stream inside macro definition. We will generate a token stream for each permutation of items
/// since the user may use arbitrary order of items in macro.
pub fn macro_item_tokens(&self) -> Vec<TokenStream> {
let res_type = self.type_name_tokens();
// We first calculate the full permutations,
let item_permutations = self.items.iter().permutations(self.items.len());
item_permutations
.map(|flag_items| {
let item_names = flag_items
.into_iter()
.map(|flag_item| flag_item.ident.clone())
.collect::<Vec<_>>();
quote! (
(#(#item_names),*) => { #res_type }
)
})
.collect()
}
}
#[derive(Clone)]
pub struct FlagItem {
/// the user provided name
ident: Ident,
/// the user-provided val
val: Expr,
}
/// generate all possible flag sets
pub fn generate_flag_sets(type_flag_def: &TypeFlagDef) -> Vec<FlagSet> {
let flag_items = type_flag_def
.items_iter()
.map(|type_flag_item| {
let ident = type_flag_item.ident();
let val = type_flag_item.val();
FlagItem { ident, val }
})
.collect::<Vec<_>>();
let flag_item_num = flag_items.len();
let limit = 1 << flag_items.len();
let mut res = Vec::with_capacity(limit);
for i in 0..limit {
let mut flag_set = FlagSet::new();
for j in 0..flag_item_num {
if (i >> j) & 0x1 == 1usize {
flag_set.push_item(flag_items[j].clone());
}
}
res.push(flag_set);
}
res
}

View File

@ -0,0 +1,63 @@
//This crate defines the procedural macro typeflags to implement capability for kxos.
//! When using this crate, kxos-typeflags-util should also be added as dependency.
//! This is due to kxos-typeflgas is a proc-macro crate, which is only allowed to export proc-macro interfaces.
//! So we leave the common type-level operations and structures defined in kxos-typeflags-util.
//!
//! type_flag is used to define another declarive macro to define type set.
//! It can be used as the following example.
//! ```rust
//! type_flags! {
//! pub trait RightSet: u32 {
//! struct Read = 1 << 1;
//! struct Write = 1 << 2;
//! }
//! }
//! ```
//! The code will generate a macro with the name as RightSet, we can use this macro to define typesets with different types.
//! Usage example:
//! ```rust
//! type O = RightSet![]; // Nil
//! type R = RightSet![Read]; // Cons<Read, Nil>
//! type W = RightSet![Write]; // Cons<Write, Nil>
//! type RW = RightSet![Read, Write]; // Cons<Write, Cons<Read, Nil>>
//! type WR = RightSet![Write, Read]; // Cons<Write, Cons<Read, Nil>>
//! ```
//!
//! Test Example
//! ```rust
//! use kxos_typeflags_util::*;
//! assert_eq!(O::BITS, 0);
//! assert_eq!(R::BITS, 2);
//! assert_eq!(W::BITS, 4);
//! assert_eq!(RW::BITS, 6);
//! assert_eq!(WR::BITS, 6);
//! assert_type_same!(SameAsOp<Read, Write>, False);
//! assert_type_same!(SameAsOp<Write, Write>, True);
//! assert_type_same!(SameAsOp<O, Nil>, True);
//! assert_type_same!(SameAsOp<RW, WR>, True);
//! assert_type_same!(SetContainOp<R, Write>, False);
//! assert_type_same!(SetContainOp<RW, Read>, True);
//! assert_type_same!(SetContainOp<O, Read>, False);
//! assert_type_same!(SetContainOp<R, Read>, True);
//! assert_type_same!(SetIncludeOp<RW, R>, True);
//! assert_type_same!(SetIncludeOp<R, W>, False);
//! assert_type_same!(SetIncludeOp<W, O>, True);
//! assert_type_same!(SetIncludeOp<O, R>, False);
//! ```
#![feature(proc_macro_diagnostic)]
#![allow(dead_code)]
use syn::parse_macro_input;
use crate::{type_flag::TypeFlagDef, util::expand_type_flag};
mod flag_set;
mod type_flag;
mod util;
#[proc_macro]
pub fn type_flags(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let type_flags_def = parse_macro_input!(input as TypeFlagDef);
expand_type_flag(&type_flags_def).into()
}

View File

@ -0,0 +1,139 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
braced,
parse::{Parse, ParseStream},
punctuated::Punctuated,
Expr, Ident, Token, Type, Visibility,
};
/// The content inside typeflag macro
pub struct TypeFlagDef {
ident: Ident,
vis: Visibility,
type_: Type,
items: Punctuated<TypeFlagItem, Token![;]>,
}
/// struct item inside typeflag macro
#[derive(Clone)]
pub struct TypeFlagItem {
vis: Visibility,
ident: Ident,
value: Expr,
}
impl Parse for TypeFlagDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let vis: Visibility = input.parse()?;
let _: Token![trait] = input.parse()?;
let ident: Ident = input.parse()?;
let _: Token![:] = input.parse()?;
let type_: Type = input.parse()?;
// read content inside brace
let content;
let _ = braced!(content in input);
let items = content.parse_terminated(TypeFlagItem::parse)?;
let res = TypeFlagDef {
ident,
vis,
type_,
items,
};
Ok(res)
}
}
impl Parse for TypeFlagItem {
fn parse(input: ParseStream) -> syn::Result<Self> {
let vis: Visibility = input.parse()?;
let _: Token![struct] = input.parse()?;
let ident: Ident = input.parse()?;
let _: Token![=] = input.parse()?;
let value: Expr = input.parse()?;
let res = TypeFlagItem { vis, ident, value };
Ok(res)
}
}
impl TypeFlagDef {
/// tokens to define the trait
pub fn trait_def_tokens(&self) -> TokenStream {
let vis = self.vis.clone();
let ident = self.ident.clone();
let type_ = self.type_.clone();
quote!(
#vis trait #ident {
const BITS: #type_;
fn new() -> Self;
}
)
}
/// tokens to define all structs
pub fn items_def_tokens(&self) -> Vec<TokenStream> {
self.items
.iter()
.map(|type_flag_item| type_flag_item.item_def_tokens())
.collect()
}
/// return the items iter
pub fn items_iter(&self) -> syn::punctuated::Iter<TypeFlagItem> {
self.items.iter()
}
/// the number of items
pub fn item_num(&self) -> usize {
self.items.len()
}
/// get item at specific position
pub fn get_item(&self, index: usize) -> Option<TypeFlagItem> {
self.items.iter().nth(index).map(|item| item.clone())
}
/// the trait ident
pub fn trait_ident(&self) -> Ident {
self.ident.clone()
}
/// the val type
pub fn val_type(&self) -> Type {
self.type_.clone()
}
}
impl TypeFlagItem {
/// the token stream to define such item
fn item_def_tokens(&self) -> TokenStream {
let vis = self.vis.clone();
let ident = self.ident.clone();
quote!(
#vis struct #ident {}
)
}
/// the item's ident
pub fn ident(&self) -> Ident {
self.ident.clone()
}
/// the expression of the items's value
pub fn val(&self) -> Expr {
self.value.clone()
}
}
impl TypeFlagDef {
/// Debug use. Print all item's name.
pub fn debug(&self) {
println!("{}", self.ident.to_string());
for type_flag_item in &self.items {
println!("{}", type_flag_item.ident.to_string());
}
}
}

View File

@ -0,0 +1,95 @@
use proc_macro2::TokenStream;
use quote::{quote, TokenStreamExt};
use crate::{
flag_set::{generate_flag_sets, FlagSet},
type_flag::TypeFlagDef,
};
pub fn expand_type_flag(type_flags_def: &TypeFlagDef) -> TokenStream {
let mut all_tokens = TokenStream::new();
let import_util_tokens = import_util();
all_tokens.append_all(import_util_tokens);
let trait_and_items_def_tokens = trait_and_items_def(type_flags_def);
all_tokens.append_all(trait_and_items_def_tokens);
let impl_same_as_tokens = impl_same_as(type_flags_def);
all_tokens.append_all(impl_same_as_tokens);
let flag_sets = generate_flag_sets(&type_flags_def);
flag_sets.iter().for_each(|flag_set| {
let impl_main_trait_tokens = flag_set.impl_main_trait_tokens(type_flags_def);
all_tokens.append_all(impl_main_trait_tokens);
});
let export_declarive_macro_tokens = export_declarive_macro(type_flags_def, &flag_sets);
all_tokens.append_all(export_declarive_macro_tokens);
all_tokens
}
/// import crate kxos_typeflags_util
pub fn import_util() -> TokenStream {
quote!(
#[macro_use]
use ::kxos_typeflags_util::*;
)
}
/// define the main trait and all items
pub fn trait_and_items_def(type_flags_def: &TypeFlagDef) -> TokenStream {
let mut tokens = TokenStream::new();
tokens.append_all(type_flags_def.trait_def_tokens());
for item_def in type_flags_def.items_def_tokens() {
tokens.append_all(item_def);
}
tokens
}
/// impl SameAs trait for each struct
pub fn impl_same_as(type_flags_def: &TypeFlagDef) -> TokenStream {
let mut all_tokens = TokenStream::new();
let items_num = type_flags_def.item_num();
for i in 0..items_num {
let item1 = type_flags_def.get_item(i).unwrap();
for j in 0..items_num {
if i == j {
// We don't need to impl SameAs for the same type
continue;
}
let item2 = type_flags_def.get_item(j).unwrap();
let ident1 = item1.ident();
let ident2 = item2.ident();
let tokens = quote!(
impl ::kxos_typeflags_util::SameAs<#ident1> for #ident2 {
type Output = ::kxos_typeflags_util::False;
}
);
all_tokens.append_all(tokens);
}
}
all_tokens
}
/// export the declarive macro
pub fn export_declarive_macro(type_flags_def: &TypeFlagDef, flag_sets: &[FlagSet]) -> TokenStream {
let macro_ident = type_flags_def.trait_ident();
let macro_item_tokens = flag_sets
.iter()
.map(|flag_set| flag_set.macro_item_tokens())
.fold(Vec::new(), |mut left, mut new_item| {
left.append(&mut new_item);
left
});
let tokens = quote!(
#[macro_export]
macro_rules! #macro_ident {
#(#macro_item_tokens);*
}
);
tokens
}