You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by GitBox <gi...@apache.org> on 2020/06/09 20:55:18 UTC

[GitHub] [incubator-tvm] robo-corg commented on a change in pull request #5527: [Rust] Second stage of Rust Refactor

robo-corg commented on a change in pull request #5527:
URL: https://github.com/apache/incubator-tvm/pull/5527#discussion_r437701038



##########
File path: rust/tvm-rt/src/function.rs
##########
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//! This module provides an idiomatic Rust API for creating and working with TVM functions.
+//!
+//! For calling an already registered TVM function use [`function::Builder`]
+//! To register a TVM packed function from Rust side either
+//! use [`function::register`] or the macro [`register_global_func`].
+//!
+//! See the tests and examples repository for more examples.
+
+use lazy_static::lazy_static;
+use std::convert::TryFrom;
+use std::{
+    collections::BTreeMap,
+    ffi::{CStr, CString},
+    mem::{self, MaybeUninit},
+    os::raw::{c_char, c_int},
+    ptr, slice, str,
+    sync::Mutex,
+};
+
+pub use tvm_sys::{ffi, ArgValue, RetValue};
+
+use crate::errors::Error;
+
+use super::to_boxed_fn::ToBoxedFn;
+use super::to_function::{ToFunction, Typed};
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+lazy_static! {
+    static ref GLOBAL_FUNCTIONS: Mutex<BTreeMap<String, Option<Function>>> = {
+        let mut out_size = 0 as c_int;
+        let mut names_ptr = ptr::null_mut() as *mut *const c_char;
+        check_call!(ffi::TVMFuncListGlobalNames(
+            &mut out_size as *mut _,
+            &mut names_ptr as *mut _,
+        ));
+        let names_list = unsafe { slice::from_raw_parts(names_ptr, out_size as usize) };
+
+        let names_list: Vec<String> =
+            names_list
+            .iter()
+            .map(|&p| unsafe { CStr::from_ptr(p).to_str().unwrap().into() })
+            .collect();
+
+        // println!("{:?}", &names_list);
+
+        let names_list = names_list
+            .into_iter()
+            .map(|p| (p, None))
+            .collect();
+
+        Mutex::new(names_list)
+    };
+}
+
+/// Wrapper around TVM function handle which includes `is_global`
+/// indicating whether the function is global or not, and `is_cloned` showing
+/// not to drop a cloned function from Rust side.
+/// The value of these fields can be accessed through their respective methods.
+#[derive(Debug, Hash)]
+pub struct Function {
+    pub(crate) handle: ffi::TVMFunctionHandle,
+    // whether the registered function is global or not.
+    is_global: bool,
+    // whether the function has been cloned from frontend or not.
+    is_cloned: bool,
+}
+
+unsafe impl Send for Function {}
+unsafe impl Sync for Function {}
+
+impl Function {
+    pub(crate) fn new(handle: ffi::TVMFunctionHandle) -> Self {
+        Function {
+            handle,
+            is_global: false,
+            is_cloned: false,
+        }
+    }
+
+    /// For a given function, it returns a function by name.
+    pub fn get<S: AsRef<str>>(name: S) -> Option<&'static Function> {
+        let mut globals = GLOBAL_FUNCTIONS.lock().unwrap();
+        globals.get_mut(name.as_ref()).and_then(|maybe_func| {
+            if maybe_func.is_none() {
+                let name = CString::new(name.as_ref()).unwrap();
+                let mut handle = ptr::null_mut() as ffi::TVMFunctionHandle;
+                check_call!(ffi::TVMFuncGetGlobal(
+                    name.as_ptr() as *const c_char,
+                    &mut handle as *mut _
+                ));
+                maybe_func.replace(Function {
+                    handle,
+                    is_global: true,
+                    is_cloned: false,
+                });
+            }
+
+            unsafe {
+                mem::transmute::<Option<&Function>, Option<&'static Function>>(maybe_func.as_ref())
+            }
+        })
+    }
+
+    /// Returns the underlying TVM function handle.
+    pub fn handle(&self) -> ffi::TVMFunctionHandle {
+        self.handle
+    }
+
+    /// Returns `true` if the underlying TVM function is global and `false` otherwise.
+    pub fn is_global(&self) -> bool {
+        self.is_global
+    }
+
+    /// Returns `true` if the underlying TVM function has been cloned
+    /// from the frontend and `false` otherwise.
+    pub fn is_cloned(&self) -> bool {
+        self.is_cloned
+    }
+
+    /// Calls the function that created from `Builder`.
+    pub fn invoke<'a>(&self, arg_buf: Vec<ArgValue<'a>>) -> Result<RetValue> {
+        let num_args = arg_buf.len();
+        let (mut values, mut type_codes): (Vec<ffi::TVMValue>, Vec<ffi::TVMTypeCode>) =
+            arg_buf.iter().map(|arg| arg.to_tvm_value()).unzip();
+
+        let mut ret_val = unsafe { MaybeUninit::uninit().assume_init() };
+        let mut ret_type_code = 0i32;
+        check_call!(ffi::TVMFuncCall(
+            self.handle,
+            values.as_mut_ptr(),
+            type_codes.as_mut_ptr() as *mut i32,
+            num_args as c_int,
+            &mut ret_val as *mut _,
+            &mut ret_type_code as *mut _
+        ));
+
+        Ok(RetValue::from_tvm_value(ret_val, ret_type_code as u32))
+    }
+
+    pub fn to_boxed_fn<F: ?Sized>(&'static self) -> Box<F>
+    where
+        F: ToBoxedFn,
+    {
+        F::to_boxed_fn(self)
+    }
+}
+
+impl Clone for Function {
+    fn clone(&self) -> Function {
+        Self {
+            handle: self.handle,
+            is_global: self.is_global,
+            is_cloned: true,
+        }
+    }
+}
+
+impl Drop for Function {
+    fn drop(&mut self) {
+        if !self.is_global && !self.is_cloned {
+            check_call!(ffi::TVMFuncFree(self.handle));
+        }
+    }
+}
+
+impl From<Function> for RetValue {
+    fn from(func: Function) -> RetValue {
+        RetValue::FuncHandle(func.handle)
+    }
+}
+
+impl TryFrom<RetValue> for Function {
+    type Error = Error;
+
+    fn try_from(ret_value: RetValue) -> Result<Function> {
+        match ret_value {
+            RetValue::FuncHandle(handle) => Ok(Function::new(handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", ret_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+impl<'a> From<Function> for ArgValue<'a> {
+    fn from(func: Function) -> ArgValue<'a> {
+        ArgValue::FuncHandle(func.handle)
+    }
+}
+
+impl<'a> TryFrom<ArgValue<'a>> for Function {
+    type Error = Error;
+
+    fn try_from(arg_value: ArgValue<'a>) -> Result<Function> {
+        match arg_value {
+            ArgValue::FuncHandle(handle) => Ok(Function::new(handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", arg_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+impl<'a> TryFrom<&ArgValue<'a>> for Function {
+    type Error = Error;
+
+    fn try_from(arg_value: &ArgValue<'a>) -> Result<Function> {
+        match arg_value {
+            ArgValue::FuncHandle(handle) => Ok(Function::new(*handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", arg_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+/// Registers a Rust function with an arbitrary type signature in
+/// the TVM registry.
+///
+///
+/// A function is convertible if and only if its arguments and return types are convertible
+/// to and from TVM values respectively.
+///
+/// Use [`register_override`] if control of overriding existing global TVM function
+/// is required, this function will panic if a function is already registered.
+///
+/// ## Example
+///
+/// ```
+/// # use tvm_rt::{ArgValue, RetValue};
+/// # use tvm_rt::function::{Function, Result, register};
+///
+/// fn sum(x: i64, y: i64, z: i64) -> i64 {
+///     x + y + z
+/// }
+///
+/// register(sum, "mysum".to_owned()).unwrap();
+/// let func = Function::get("mysum").unwrap();
+/// let boxed_fn = func.to_boxed_fn::<dyn Fn(i64, i64, i64) -> Result<i64>>();
+/// let ret = boxed_fn(10, 20, 30).unwrap();
+/// assert_eq!(ret, 60);
+/// ```
+pub fn register<F, I, O, S: Into<String>>(f: F, name: S) -> Result<()>
+where
+    F: ToFunction<I, O>,
+    F: Typed<I, O>,
+{
+    register_override(f, name, false)
+}
+
+/// Register a function with explicit control over whether to override an existing registration or not.
+///
+/// See `register` for more details on how to use the registration API.
+pub fn register_override<F, I, O, S: Into<String>>(f: F, name: S, override_: bool) -> Result<()>
+where
+    F: ToFunction<I, O>,
+    F: Typed<I, O>,
+{
+    let func = f.to_function();
+    let name = name.into();
+    let mut globals = GLOBAL_FUNCTIONS.lock().unwrap();
+    // Not sure about this code
+    let handle = func.handle();
+    globals.insert(name.clone(), Some(func));

Review comment:
       What about existing &'static Function handed out? It seems like the best case is a non-mut ref is updated, worst case this reference is now pointing at something else when the BTree updates? 

##########
File path: rust/macros/src/object.rs
##########
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::quote;
+use syn::DeriveInput;
+use syn::Ident;
+
+pub fn macro_impl(input: proc_macro::TokenStream) -> TokenStream {
+    let derive_input = syn::parse_macro_input!(input as DeriveInput);
+    let payload_id = derive_input.ident;
+
+    let mut type_key = None;
+    let mut ref_name = None;
+    let base = Some(Ident::new("base", Span::call_site()));
+
+    for attr in derive_input.attrs {
+        if attr.path.is_ident("type_key") {
+            type_key = Some(attr.parse_meta().expect("foo"))
+        }
+
+        if attr.path.is_ident("ref_name") {
+            ref_name = Some(attr.parse_meta().expect("foo"))
+        }
+    }
+
+    let type_key = if let Some(syn::Meta::NameValue(name_value)) = type_key {
+        match name_value.lit {
+            syn::Lit::Str(type_key) => type_key,
+            _ => panic!("foo"),
+        }
+    } else {
+        panic!("bar");
+    };
+
+    let ref_name = if let Some(syn::Meta::NameValue(name_value)) = ref_name {
+        match name_value.lit {
+            syn::Lit::Str(ref_name) => ref_name,
+            _ => panic!("foo"),
+        }
+    } else {
+        panic!("bar");
+    };
+
+    let ref_id = Ident::new(&ref_name.value(), Span::call_site());
+    let base = base.expect("should be present");
+
+    let expanded = quote! {
+        unsafe impl tvm_rt::object::IsObject for #payload_id {
+            const TYPE_KEY: &'static str = #type_key;
+
+            fn as_object<'s>(&'s self) -> &'s Object {
+                &self.#base.as_object()
+            }
+        }
+
+        #[derive(Clone)]
+        pub struct #ref_id(Option<tvm_rt::object::ObjectPtr<#payload_id>>);
+
+        impl tvm_rt::object::ToObjectRef for #ref_id {
+            fn to_object_ref(&self) -> ObjectRef {
+                ObjectRef(self.0.as_ref().map(|o| o.upcast()))
+            }
+        }
+
+        impl std::ops::Deref for #ref_id {
+            type Target = #payload_id;
+
+            fn deref(&self) -> &Self::Target {
+                self.0.as_ref().unwrap()
+            }
+        }
+
+        impl std::convert::TryFrom<tvm_rt::RetValue> for #ref_id {
+            type Error = ::anyhow::Error;
+
+            fn try_from(ret_val: tvm_rt::RetValue) -> Result<#ref_id, Self::Error> {
+                use std::convert::TryInto;
+                let oref: ObjectRef = ret_val.try_into()?;
+                let ptr = oref.0.ok_or(anyhow::anyhow!("null ptr"))?;

Review comment:
       Is there reason the following does not work
   ```suggestion
                   let ptr = oref.0.context("null ptr")?;
   ```
   macro hygiene?
   

##########
File path: rust/macros/src/object.rs
##########
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::quote;
+use syn::DeriveInput;
+use syn::Ident;
+
+pub fn macro_impl(input: proc_macro::TokenStream) -> TokenStream {
+    let derive_input = syn::parse_macro_input!(input as DeriveInput);
+    let payload_id = derive_input.ident;
+
+    let mut type_key = None;
+    let mut ref_name = None;
+    let base = Some(Ident::new("base", Span::call_site()));
+
+    for attr in derive_input.attrs {
+        if attr.path.is_ident("type_key") {
+            type_key = Some(attr.parse_meta().expect("foo"))
+        }
+
+        if attr.path.is_ident("ref_name") {
+            ref_name = Some(attr.parse_meta().expect("foo"))
+        }
+    }
+
+    let type_key = if let Some(syn::Meta::NameValue(name_value)) = type_key {
+        match name_value.lit {
+            syn::Lit::Str(type_key) => type_key,
+            _ => panic!("foo"),
+        }
+    } else {
+        panic!("bar");
+    };
+
+    let ref_name = if let Some(syn::Meta::NameValue(name_value)) = ref_name {
+        match name_value.lit {
+            syn::Lit::Str(ref_name) => ref_name,
+            _ => panic!("foo"),
+        }
+    } else {
+        panic!("bar");
+    };

Review comment:
       Add proper error messages in panics?

##########
File path: rust/macros/src/import_module.rs
##########
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+use quote::quote;
+use std::{fs::File, io::Read};
+use syn::parse::{Parse, ParseStream, Result};
+use syn::LitStr;
+
+use std::path::PathBuf;
+
+struct ImportModule {
+    importing_file: LitStr,
+}
+
+impl Parse for ImportModule {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let importing_file: LitStr = input.parse()?;
+        Ok(ImportModule { importing_file })
+    }
+}
+
+pub fn macro_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let import_module_args = syn::parse_macro_input!(input as ImportModule);
+
+    let manifest =
+        std::env::var("CARGO_MANIFEST_DIR").expect("variable should always be set by Cargo.");
+
+    let mut path = PathBuf::new();
+    path.push(manifest);
+    path = path.join(import_module_args.importing_file.value());
+
+    let mut fd = File::open(&path)
+        .unwrap_or_else(|_| panic!("Unable to find TVM object file at `{}`", path.display()));
+    let mut buffer = Vec::new();
+    fd.read_to_end(&mut buffer).unwrap();

Review comment:
       It might be neat to follow up with https://crates.io/crates/proc-macro-error to make these messages nicer.

##########
File path: rust/tvm-rt/src/object/object_ptr.rs
##########
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use std::convert::TryFrom;
+use std::ffi::CString;
+use std::ptr::NonNull;
+
+use tvm_sys::ffi::{self, /* TVMObjectFree, */ TVMObjectRetain, TVMObjectTypeKey2Index};
+use tvm_sys::{ArgValue, RetValue};
+
+use crate::errors::Error;
+
+type Deleter<T> = unsafe extern "C" fn(object: *mut T) -> ();
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct Object {
+    pub type_index: u32,
+    pub ref_count: i32,
+    pub fdeleter: Deleter<Object>,

Review comment:
       Should these fields be pub?

##########
File path: rust/tvm-rt/src/object/object_ptr.rs
##########
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use std::convert::TryFrom;
+use std::ffi::CString;
+use std::ptr::NonNull;
+
+use tvm_sys::ffi::{self, /* TVMObjectFree, */ TVMObjectRetain, TVMObjectTypeKey2Index};
+use tvm_sys::{ArgValue, RetValue};
+
+use crate::errors::Error;
+
+type Deleter<T> = unsafe extern "C" fn(object: *mut T) -> ();
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct Object {
+    pub type_index: u32,
+    pub ref_count: i32,
+    pub fdeleter: Deleter<Object>,
+}
+
+unsafe extern "C" fn delete<T: IsObject>(object: *mut Object) {
+    let typed_object: *mut T = std::mem::transmute(object);
+    T::typed_delete(typed_object);
+}
+
+fn derived_from(child_type_index: u32, parent_type_index: u32) -> bool {
+    let mut is_derived = 0;
+    crate::check_call!(ffi::TVMObjectDerivedFrom(
+        child_type_index,
+        parent_type_index,
+        &mut is_derived
+    ));
+
+    if is_derived == 0 {
+        false
+    } else {
+        true
+    }
+}
+
+impl Object {
+    fn new(type_index: u32, deleter: Deleter<Object>) -> Object {

Review comment:
       Make this pub and remove pub fields from object?

##########
File path: rust/tvm-rt/src/function.rs
##########
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//! This module provides an idiomatic Rust API for creating and working with TVM functions.
+//!
+//! For calling an already registered TVM function use [`function::Builder`]
+//! To register a TVM packed function from Rust side either
+//! use [`function::register`] or the macro [`register_global_func`].
+//!
+//! See the tests and examples repository for more examples.
+
+use std::{
+    collections::BTreeMap,
+    ffi::{CStr, CString},
+    mem::{self, MaybeUninit},
+    os::raw::{c_char, c_int},
+    ptr, slice, str,
+    sync::Mutex,
+};
+
+use anyhow::{Result};
+use lazy_static::lazy_static;
+
+pub use tvm_sys::{ffi, ArgValue, RetValue};
+
+use super::to_function::{ToFunction, Typed};
+use super::to_boxed_fn::ToBoxedFn;
+
+lazy_static! {
+    static ref GLOBAL_FUNCTIONS: Mutex<BTreeMap<String, Option<Function>>> = {
+        let mut out_size = 0 as c_int;
+        let mut names_ptr = ptr::null_mut() as *mut *const c_char;
+        check_call!(ffi::TVMFuncListGlobalNames(
+            &mut out_size as *mut _,
+            &mut names_ptr as *mut _,
+        ));
+        let names_list = unsafe { slice::from_raw_parts(names_ptr, out_size as usize) };
+
+        let names_list: Vec<String> =
+            names_list
+            .iter()
+            .map(|&p| unsafe { CStr::from_ptr(p).to_str().unwrap().into() })
+            .collect();
+
+        // println!("{:?}", &names_list);

Review comment:
       Delete
   ```suggestion
   ```

##########
File path: rust/tvm-rt/src/value.rs
##########
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//! This module implements [`ArgValue`] and [`RetValue`] types
+//! and their conversions needed for the types used in frontend crate.
+//! `RetValue` is the owned version of `TVMPODValue`.
+
+use std::convert::TryFrom;
+// use std::ffi::c_void;

Review comment:
       ```suggestion
   ```
   Delete

##########
File path: rust/tvm-rt/src/function.rs
##########
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//! This module provides an idiomatic Rust API for creating and working with TVM functions.
+//!
+//! For calling an already registered TVM function use [`function::Builder`]
+//! To register a TVM packed function from Rust side either
+//! use [`function::register`] or the macro [`register_global_func`].
+//!
+//! See the tests and examples repository for more examples.
+
+use lazy_static::lazy_static;
+use std::convert::TryFrom;
+use std::{
+    collections::BTreeMap,
+    ffi::{CStr, CString},
+    mem::{self, MaybeUninit},
+    os::raw::{c_char, c_int},
+    ptr, slice, str,
+    sync::Mutex,
+};
+
+pub use tvm_sys::{ffi, ArgValue, RetValue};
+
+use crate::errors::Error;
+
+use super::to_boxed_fn::ToBoxedFn;
+use super::to_function::{ToFunction, Typed};
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+lazy_static! {
+    static ref GLOBAL_FUNCTIONS: Mutex<BTreeMap<String, Option<Function>>> = {
+        let mut out_size = 0 as c_int;
+        let mut names_ptr = ptr::null_mut() as *mut *const c_char;
+        check_call!(ffi::TVMFuncListGlobalNames(
+            &mut out_size as *mut _,
+            &mut names_ptr as *mut _,
+        ));
+        let names_list = unsafe { slice::from_raw_parts(names_ptr, out_size as usize) };
+
+        let names_list: Vec<String> =
+            names_list
+            .iter()
+            .map(|&p| unsafe { CStr::from_ptr(p).to_str().unwrap().into() })
+            .collect();
+
+        // println!("{:?}", &names_list);
+
+        let names_list = names_list
+            .into_iter()
+            .map(|p| (p, None))
+            .collect();
+
+        Mutex::new(names_list)
+    };
+}
+
+/// Wrapper around TVM function handle which includes `is_global`
+/// indicating whether the function is global or not, and `is_cloned` showing
+/// not to drop a cloned function from Rust side.
+/// The value of these fields can be accessed through their respective methods.
+#[derive(Debug, Hash)]
+pub struct Function {
+    pub(crate) handle: ffi::TVMFunctionHandle,
+    // whether the registered function is global or not.
+    is_global: bool,
+    // whether the function has been cloned from frontend or not.
+    is_cloned: bool,
+}
+
+unsafe impl Send for Function {}
+unsafe impl Sync for Function {}
+
+impl Function {
+    pub(crate) fn new(handle: ffi::TVMFunctionHandle) -> Self {
+        Function {
+            handle,
+            is_global: false,
+            is_cloned: false,
+        }
+    }
+
+    /// For a given function, it returns a function by name.
+    pub fn get<S: AsRef<str>>(name: S) -> Option<&'static Function> {
+        let mut globals = GLOBAL_FUNCTIONS.lock().unwrap();
+        globals.get_mut(name.as_ref()).and_then(|maybe_func| {
+            if maybe_func.is_none() {
+                let name = CString::new(name.as_ref()).unwrap();
+                let mut handle = ptr::null_mut() as ffi::TVMFunctionHandle;
+                check_call!(ffi::TVMFuncGetGlobal(
+                    name.as_ptr() as *const c_char,
+                    &mut handle as *mut _
+                ));
+                maybe_func.replace(Function {
+                    handle,
+                    is_global: true,
+                    is_cloned: false,
+                });
+            }
+
+            unsafe {
+                mem::transmute::<Option<&Function>, Option<&'static Function>>(maybe_func.as_ref())
+            }
+        })
+    }
+
+    /// Returns the underlying TVM function handle.
+    pub fn handle(&self) -> ffi::TVMFunctionHandle {
+        self.handle
+    }
+
+    /// Returns `true` if the underlying TVM function is global and `false` otherwise.
+    pub fn is_global(&self) -> bool {
+        self.is_global
+    }
+
+    /// Returns `true` if the underlying TVM function has been cloned
+    /// from the frontend and `false` otherwise.
+    pub fn is_cloned(&self) -> bool {
+        self.is_cloned
+    }
+
+    /// Calls the function that created from `Builder`.
+    pub fn invoke<'a>(&self, arg_buf: Vec<ArgValue<'a>>) -> Result<RetValue> {
+        let num_args = arg_buf.len();
+        let (mut values, mut type_codes): (Vec<ffi::TVMValue>, Vec<ffi::TVMTypeCode>) =
+            arg_buf.iter().map(|arg| arg.to_tvm_value()).unzip();
+
+        let mut ret_val = unsafe { MaybeUninit::uninit().assume_init() };
+        let mut ret_type_code = 0i32;
+        check_call!(ffi::TVMFuncCall(
+            self.handle,
+            values.as_mut_ptr(),
+            type_codes.as_mut_ptr() as *mut i32,
+            num_args as c_int,
+            &mut ret_val as *mut _,
+            &mut ret_type_code as *mut _
+        ));
+
+        Ok(RetValue::from_tvm_value(ret_val, ret_type_code as u32))
+    }
+
+    pub fn to_boxed_fn<F: ?Sized>(&'static self) -> Box<F>
+    where
+        F: ToBoxedFn,
+    {
+        F::to_boxed_fn(self)
+    }
+}
+
+impl Clone for Function {
+    fn clone(&self) -> Function {
+        Self {
+            handle: self.handle,
+            is_global: self.is_global,
+            is_cloned: true,
+        }
+    }
+}
+
+impl Drop for Function {
+    fn drop(&mut self) {
+        if !self.is_global && !self.is_cloned {
+            check_call!(ffi::TVMFuncFree(self.handle));
+        }
+    }
+}
+
+impl From<Function> for RetValue {
+    fn from(func: Function) -> RetValue {
+        RetValue::FuncHandle(func.handle)
+    }
+}
+
+impl TryFrom<RetValue> for Function {
+    type Error = Error;
+
+    fn try_from(ret_value: RetValue) -> Result<Function> {
+        match ret_value {
+            RetValue::FuncHandle(handle) => Ok(Function::new(handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", ret_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+impl<'a> From<Function> for ArgValue<'a> {
+    fn from(func: Function) -> ArgValue<'a> {
+        ArgValue::FuncHandle(func.handle)
+    }
+}
+
+impl<'a> TryFrom<ArgValue<'a>> for Function {
+    type Error = Error;
+
+    fn try_from(arg_value: ArgValue<'a>) -> Result<Function> {
+        match arg_value {
+            ArgValue::FuncHandle(handle) => Ok(Function::new(handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", arg_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+impl<'a> TryFrom<&ArgValue<'a>> for Function {
+    type Error = Error;
+
+    fn try_from(arg_value: &ArgValue<'a>) -> Result<Function> {
+        match arg_value {
+            ArgValue::FuncHandle(handle) => Ok(Function::new(*handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", arg_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+/// Registers a Rust function with an arbitrary type signature in
+/// the TVM registry.
+///
+///
+/// A function is convertible if and only if its arguments and return types are convertible
+/// to and from TVM values respectively.
+///
+/// Use [`register_override`] if control of overriding existing global TVM function
+/// is required, this function will panic if a function is already registered.
+///
+/// ## Example
+///
+/// ```
+/// # use tvm_rt::{ArgValue, RetValue};
+/// # use tvm_rt::function::{Function, Result, register};
+///
+/// fn sum(x: i64, y: i64, z: i64) -> i64 {
+///     x + y + z
+/// }
+///
+/// register(sum, "mysum".to_owned()).unwrap();
+/// let func = Function::get("mysum").unwrap();
+/// let boxed_fn = func.to_boxed_fn::<dyn Fn(i64, i64, i64) -> Result<i64>>();
+/// let ret = boxed_fn(10, 20, 30).unwrap();
+/// assert_eq!(ret, 60);
+/// ```
+pub fn register<F, I, O, S: Into<String>>(f: F, name: S) -> Result<()>
+where
+    F: ToFunction<I, O>,
+    F: Typed<I, O>,
+{
+    register_override(f, name, false)
+}
+
+/// Register a function with explicit control over whether to override an existing registration or not.
+///
+/// See `register` for more details on how to use the registration API.
+pub fn register_override<F, I, O, S: Into<String>>(f: F, name: S, override_: bool) -> Result<()>
+where
+    F: ToFunction<I, O>,
+    F: Typed<I, O>,
+{
+    let func = f.to_function();
+    let name = name.into();
+    let mut globals = GLOBAL_FUNCTIONS.lock().unwrap();
+    // Not sure about this code
+    let handle = func.handle();
+    globals.insert(name.clone(), Some(func));
+    let name = CString::new(name)?;
+    check_call!(ffi::TVMFuncRegisterGlobal(
+        name.into_raw(),
+        handle,
+        override_ as c_int
+    ));
+
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::function::Function;
+
+    static CANARY: &str = "runtime.ModuleLoadFromFile";
+
+    // #[test]
+    // fn list_global_func() {
+    //     assert!(GLOBAL_FUNCTIONS.lock().unwrap().contains_key(CANARY));
+    // }
+
+    #[test]
+    fn get_fn() {
+        assert!(Function::get(CANARY).is_some());
+        assert!(Function::get("does not exists!").is_none());
+    }
+
+    #[test]
+    fn register_and_call_closure0() {
+        use crate::function;
+        use function::Result;
+
+        fn constfn() -> i64 {
+            return 10;
+        }
+
+        function::register_override(constfn, "constfn".to_owned(), true).unwrap();
+        let func = Function::get("constfn").unwrap();
+        let func = func.to_boxed_fn::<dyn Fn() -> Result<i32>>();
+        let ret = func().unwrap();
+        assert_eq!(ret, 10);
+    }
+

Review comment:
       ```suggestion
       #[test]
       fn register_and_call_original_closure1() {
           use crate::function::{self};
   
           fn ident(x: i64) -> i64 {
               return x;
           }
           
           let func = Function::get("ident").unwrap();
   
           function::register_override(ident, "ident".to_owned(), false).unwrap();
           
           // What is func now?
           let func = func.to_boxed_fn::<dyn Fn(i32) -> Result<i32>>();
           assert_eq!(func(60).unwrap(), 60);
       }
   ```

##########
File path: rust/tvm-rt/README.md
##########
@@ -0,0 +1,60 @@
+<!--- Licensed to the Apache Software Foundation (ASF) under one -->
+<!--- or more contributor license agreements.  See the NOTICE file -->
+<!--- distributed with this work for additional information -->
+<!--- regarding copyright ownership.  The ASF licenses this file -->
+<!--- to you under the Apache License, Version 2.0 (the -->
+<!--- "License"); you may not use this file except in compliance -->
+<!--- with the License.  You may obtain a copy of the License at -->
+
+<!---   http://www.apache.org/licenses/LICENSE-2.0 -->
+
+<!--- Unless required by applicable law or agreed to in writing, -->
+<!--- software distributed under the License is distributed on an -->
+<!--- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -->
+<!--- KIND, either express or implied.  See the License for the -->
+<!--- specific language governing permissions and limitations -->
+<!--- under the License. -->
+
+# TVM Runtime Support
+
+This crate provides an idiomatic Rust API for [TVM](https://github.com/apache/incubator-tvm) runtime.
+Currently this is tested on `1.42.0` and above.
+
+## What Does This Crate Offer?
+
+TVM is an end-to-end deep learning compiler which takes high level machine learning
+models or tensor computations and lowers them into executable code for a variety
+of heterogenous devices (e.g., CPU, GPU).
+
+This crate provides access to the APIs for manipulating runtime data structures,
+as well as TVM's cross-language Object system which functions similarly to systems
+such as COM, enabling cross-language interoperability.
+
+## Installations
+
+Please follow TVM [installation](https://tvm.apache.org/docs/install/index.html) instructions,
+`export TVM_HOME=/path/to/tvm` and add `libtvm_runtime` to your `LD_LIBRARY_PATH`.
+
+### Example of registering a cross-language closure.
+
+One can use `register!` macro to expose a Rust closure with arguments which implement `TryFrom<ArgValue>`
+and return types which implement `Into<RetValue>`. Once registered with TVM these functions can be
+accessed via Python or C++, or any other language which implements the packed function convention
+see `docs.tvm.ai` for more information.
+
+```rust
+use tvm_rt::{ArgValue, RetValue};
+use tvm_rt::function::{Function, Result, register};
+
+fn sum(x: i64, y: i64, z: i64) -> i64 {
+    x + y + z
+}
+
+fn main() {
+    register(sum, "mysum".to_owned()).unwrap();

Review comment:
       ```suggestion
       register!(sum, "mysum".to_owned()).unwrap();
   ```

##########
File path: rust/tvm-rt/src/to_function.rs
##########
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//! This module provides an idiomatic Rust API for creating and working with TVM functions.
+//!
+//! For calling an already registered TVM function use [`function::Builder`]
+//! To register a TVM packed function from Rust side either
+//! use [`function::register`] or the macro [`register_global_func`].
+//!
+//! See the tests and examples repository for more examples.
+
+use std::convert::{TryFrom, TryInto};
+use std::{
+    mem::MaybeUninit,
+    os::raw::{c_int, c_void},
+    ptr, slice,
+};
+
+use super::{function::Result, Function};
+use crate::errors::Error;
+
+pub use tvm_sys::{ffi, ArgValue, RetValue};
+
+/// A trait representing whether the function arguments
+/// and return type can be assigned to a TVM packed function.
+///
+/// By splitting the conversion to function into two traits
+/// we are able to improve error reporting, by splitting the
+/// conversion of inputs and outputs to this trait.
+///
+/// And the implementation of it to `ToFunction`.
+pub trait Typed<I, O> {
+    fn args(i: &[ArgValue<'static>]) -> Result<I>;
+    fn ret(o: O) -> RetValue;
+}
+
+impl<F, O: Into<RetValue>> Typed<(), O> for F
+where
+    F: Fn() -> O,
+{
+    fn args(_args: &[ArgValue<'static>]) -> Result<()> {
+        debug_assert!(_args.len() == 0);
+        Ok(())
+    }
+
+    fn ret(o: O) -> RetValue {
+        o.into()
+    }
+}
+
+impl<F, A, O: Into<RetValue>, E> Typed<(A,), O> for F
+where
+    F: Fn(A) -> O,
+    Error: From<E>,
+    A: TryFrom<ArgValue<'static>, Error = E>,
+{
+    fn args(args: &[ArgValue<'static>]) -> Result<(A,)> {
+        debug_assert!(args.len() == 1);
+        let a: A = args[0].clone().try_into()?;
+        Ok((a,))
+    }
+
+    fn ret(o: O) -> RetValue {
+        o.into()
+    }
+}
+
+impl<F, A, B, O: Into<RetValue>, E> Typed<(A, B), O> for F
+where
+    F: Fn(A, B) -> O,
+    Error: From<E>,
+    A: TryFrom<ArgValue<'static>, Error = E>,
+    B: TryFrom<ArgValue<'static>, Error = E>,
+{
+    fn args(args: &[ArgValue<'static>]) -> Result<(A, B)> {
+        debug_assert!(args.len() == 2);
+        let a: A = args[0].clone().try_into()?;
+        let b: B = args[1].clone().try_into()?;
+        Ok((a, b))
+    }
+
+    fn ret(o: O) -> RetValue {
+        o.into()
+    }
+}
+
+impl<F, A, B, C, O: Into<RetValue>, E> Typed<(A, B, C), O> for F
+where
+    F: Fn(A, B, C) -> O,
+    Error: From<E>,
+    A: TryFrom<ArgValue<'static>, Error = E>,
+    B: TryFrom<ArgValue<'static>, Error = E>,
+    C: TryFrom<ArgValue<'static>, Error = E>,
+{
+    fn args(args: &[ArgValue<'static>]) -> Result<(A, B, C)> {
+        debug_assert!(args.len() == 3);
+        let a: A = args[0].clone().try_into()?;
+        let b: B = args[1].clone().try_into()?;
+        let c: C = args[2].clone().try_into()?;
+        Ok((a, b, c))
+    }
+
+    fn ret(o: O) -> RetValue {
+        o.into()
+    }
+}
+
+pub trait ToFunction<I, O>: Sized {
+    type Handle;
+
+    fn into_raw(self) -> *mut Self::Handle;
+
+    fn call(handle: *mut Self::Handle, args: &[ArgValue<'static>]) -> Result<RetValue>
+    where
+        Self: Typed<I, O>;
+
+    fn drop(handle: *mut Self::Handle);
+
+    fn to_function(self) -> Function
+    where
+        Self: Typed<I, O>,
+    {
+        let mut fhandle = ptr::null_mut() as ffi::TVMFunctionHandle;
+        let resource_handle = self.into_raw();
+        check_call!(ffi::TVMFuncCreateFromCFunc(
+            Some(Self::tvm_callback),
+            resource_handle as *mut _,
+            Some(Self::tvm_finalizer),
+            &mut fhandle as *mut _
+        ));
+        Function::new(fhandle)
+    }
+
+    /// The callback function which is wrapped converted by TVM
+    /// into a packed function stored in fhandle.
+    unsafe extern "C" fn tvm_callback(
+        args: *mut ffi::TVMValue,
+        type_codes: *mut c_int,
+        num_args: c_int,
+        ret: ffi::TVMRetValueHandle,
+        fhandle: *mut c_void,
+    ) -> c_int
+    where
+        Self: Typed<I, O>,
+    {
+        // turning off the incorrect linter complaints
+        #![allow(unused_assignments, unused_unsafe)]
+        let len = num_args as usize;
+        let args_list = slice::from_raw_parts_mut(args, len);
+        let type_codes_list = slice::from_raw_parts_mut(type_codes, len);
+        let mut local_args: Vec<ArgValue> = Vec::new();
+        let mut value = MaybeUninit::uninit().assume_init();
+        let mut tcode = MaybeUninit::uninit().assume_init();
+        let rust_fn = fhandle as *mut Self::Handle;
+        for i in 0..len {
+            value = args_list[i];
+            println!("{:?}", value.v_handle);
+            tcode = type_codes_list[i];
+            if tcode == ffi::TVMTypeCode_kTVMObjectHandle as c_int
+                || tcode == ffi::TVMTypeCode_kTVMPackedFuncHandle as c_int
+                || tcode == ffi::TVMTypeCode_kTVMModuleHandle as c_int
+            {
+                check_call!(ffi::TVMCbArgToReturn(
+                    &mut value as *mut _,
+                    &mut tcode as *mut _
+                ));
+                println!("{:?}", value.v_handle);
+            }
+            let arg_value = ArgValue::from_tvm_value(value, tcode as u32);
+            println!("{:?}", arg_value);
+            local_args.push(arg_value);
+        }
+
+        let rv = match Self::call(rust_fn, local_args.as_slice()) {
+            Ok(v) => v,
+            Err(msg) => {
+                crate::set_last_error(&msg);
+                return -1;
+            }
+        };
+
+        let (mut ret_val, ret_tcode) = rv.to_tvm_value();
+        let mut ret_type_code = ret_tcode as c_int;
+        check_call!(ffi::TVMCFuncSetReturn(
+            ret,
+            &mut ret_val as *mut _,
+            &mut ret_type_code as *mut _,
+            1 as c_int
+        ));
+        0
+    }
+
+    /// The finalizer which is invoked when the packed function's
+    /// reference count is zero.
+    unsafe extern "C" fn tvm_finalizer(fhandle: *mut c_void) {
+        let handle = std::mem::transmute(fhandle);
+        Self::drop(handle)
+    }
+}
+
+// /// A wrapper that is used to work around inference issues for bare functions.
+// ///
+// /// Used to implement `register_untyped`.
+// pub(self) struct RawFunction {
+//     fn_ptr: for<'a> fn (&'a [ArgValue<'static>]) -> Result<RetValue>
+// }
+
+// impl RawFunction {
+//     fn new(fn_ptr: for<'a> fn (&'a [ArgValue<'static>]) -> Result<RetValue>) -> RawFunction {
+//         RawFunction { fn_ptr: fn_ptr }
+//     }
+// }
+
+// impl Typed<&[ArgValue<'static>], ()> for RawFunction {
+//     fn args(i: &[ArgValue<'static>]) -> Result<&[ArgValue<'static>]> {
+//         Ok(i)
+//     }
+
+//     fn ret(o: O) -> RetValue;
+// }
+
+// impl ToFunction<(), ()> for RawFunction
+// {
+//     type Handle = fn(&[ArgValue<'static>]) -> Result<RetValue>;
+
+//     fn into_raw(self) -> *mut Self::Handle {
+//         self.fn_ptr as *mut Self::Handle
+//     }
+
+//     fn call(handle: *mut Self::Handle, args: &[ArgValue<'static>]) -> Result<RetValue> {
+//         let handle: Self::Handle = unsafe { std::mem::transmute(handle) };
+//         let r = handle(args);
+//         println!("afters");
+//         r
+//     }
+
+//     // Function's don't need de-allocation because the pointers are into the code section of memory.
+//     fn drop(_: *mut Self::Handle) {}
+// }

Review comment:
       ```suggestion
   ```
   Delete commented code

##########
File path: rust/macros/src/object.rs
##########
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::quote;
+use syn::DeriveInput;
+use syn::Ident;
+
+pub fn macro_impl(input: proc_macro::TokenStream) -> TokenStream {
+    let derive_input = syn::parse_macro_input!(input as DeriveInput);
+    let payload_id = derive_input.ident;
+
+    let mut type_key = None;
+    let mut ref_name = None;
+    let base = Some(Ident::new("base", Span::call_site()));
+
+    for attr in derive_input.attrs {
+        if attr.path.is_ident("type_key") {
+            type_key = Some(attr.parse_meta().expect("foo"))
+        }
+
+        if attr.path.is_ident("ref_name") {
+            ref_name = Some(attr.parse_meta().expect("foo"))
+        }
+    }
+
+    let type_key = if let Some(syn::Meta::NameValue(name_value)) = type_key {
+        match name_value.lit {
+            syn::Lit::Str(type_key) => type_key,
+            _ => panic!("foo"),
+        }
+    } else {
+        panic!("bar");
+    };
+
+    let ref_name = if let Some(syn::Meta::NameValue(name_value)) = ref_name {
+        match name_value.lit {
+            syn::Lit::Str(ref_name) => ref_name,
+            _ => panic!("foo"),
+        }
+    } else {
+        panic!("bar");
+    };
+
+    let ref_id = Ident::new(&ref_name.value(), Span::call_site());
+    let base = base.expect("should be present");
+
+    let expanded = quote! {
+        unsafe impl tvm_rt::object::IsObject for #payload_id {
+            const TYPE_KEY: &'static str = #type_key;
+
+            fn as_object<'s>(&'s self) -> &'s Object {
+                &self.#base.as_object()
+            }
+        }
+
+        #[derive(Clone)]
+        pub struct #ref_id(Option<tvm_rt::object::ObjectPtr<#payload_id>>);
+
+        impl tvm_rt::object::ToObjectRef for #ref_id {
+            fn to_object_ref(&self) -> ObjectRef {
+                ObjectRef(self.0.as_ref().map(|o| o.upcast()))
+            }
+        }
+
+        impl std::ops::Deref for #ref_id {
+            type Target = #payload_id;
+
+            fn deref(&self) -> &Self::Target {
+                self.0.as_ref().unwrap()
+            }
+        }
+
+        impl std::convert::TryFrom<tvm_rt::RetValue> for #ref_id {
+            type Error = ::anyhow::Error;

Review comment:
       I agree with @adelbertc that a bespoke error here makes more sense than anyhow.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org