Skip to content
8 changes: 2 additions & 6 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@ use crate::prelude::{
collections::BTreeMap,
marker::PhantomData,
string::String,
vec,
vec::Vec,
};

use crate::{
build::*,
meta_type,
MetaType,
Path,
Type,
TypeDefArray,
TypeDefPhantom,
TypeDefPrimitive,
TypeDefSequence,
TypeDefTuple,
Expand Down Expand Up @@ -249,9 +248,6 @@ where
type Identity = Self;

fn type_info() -> Type {
Type::builder()
.path(Path::prelude("PhantomData"))
.type_params(vec![meta_type::<T>()])
.composite(Fields::unit())
TypeDefPhantom::new(MetaType::new::<T>()).into()
}
}
38 changes: 31 additions & 7 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,7 @@ fn prelude_items() {
.variant("Err", Fields::unnamed().field_of::<String>("E"))
)
);
assert_type!(
PhantomData<i32>,
Type::builder()
.path(Path::prelude("PhantomData"))
.type_params(tuple_meta_type!(i32))
.composite(Fields::unit())
);
assert_type!(PhantomData<i32>, TypeDefPhantom::new(meta_type::<i32>()));
}

#[test]
Expand Down Expand Up @@ -154,3 +148,33 @@ fn struct_with_generics() {
.composite(Fields::named().field_of::<Box<MyStruct<bool>>>("data", "T"));
assert_type!(SelfTyped, expected_type);
}

#[test]
fn basic_struct_with_phantoms() {
#[allow(unused)]
struct SomeStruct<T> {
a: u8,
marker: PhantomData<T>,
}

impl<T> TypeInfo for SomeStruct<T>
where
T: TypeInfo + 'static,
{
type Identity = Self;

fn type_info() -> Type {
Type::builder()
.path(Path::new("SomeStruct", module_path!()))
.type_params(tuple_meta_type!(T))
.composite(Fields::named().field_of::<u8>("a", "u8"))
}
}

let struct_bool_type_info = Type::builder()
.path(Path::from_segments(vec!["scale_info", "tests", "SomeStruct"]).unwrap())
.type_params(tuple_meta_type!(bool))
.composite(Fields::named().field_of::<u8>("a", "u8"));

assert_type!(SomeStruct<bool>, struct_bool_type_info);
}
61 changes: 61 additions & 0 deletions src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ impl From<TypeDefTuple> for Type {
}
}

impl From<TypeDefPhantom> for Type {
fn from(phantom: TypeDefPhantom) -> Self {
Self::new(Path::voldemort(), Vec::new(), phantom)
}
}

impl Type {
/// Create a [`TypeBuilder`](`crate::build::TypeBuilder`) the public API for constructing a [`Type`]
pub fn builder() -> TypeBuilder {
Expand Down Expand Up @@ -181,6 +187,8 @@ pub enum TypeDef<T: Form = MetaForm> {
Tuple(TypeDefTuple<T>),
/// A Rust primitive type.
Primitive(TypeDefPrimitive),
/// A PhantomData type.
Phantom(TypeDefPhantom<T>),
}

impl IntoPortable for TypeDef {
Expand All @@ -194,6 +202,7 @@ impl IntoPortable for TypeDef {
TypeDef::Array(array) => array.into_portable(registry).into(),
TypeDef::Tuple(tuple) => tuple.into_portable(registry).into(),
TypeDef::Primitive(primitive) => primitive.into(),
TypeDef::Phantom(phantom) => phantom.into_portable(registry).into(),
}
}
}
Expand Down Expand Up @@ -395,3 +404,55 @@ where
&self.type_param
}
}

/// A type describing a `PhantomData<T>` type.
///
/// In the context of SCALE encoded types, including `PhantomData<T>` types in
/// the type info might seem surprising. The reason to include this information
/// is that there could be situations where it's useful and because removing
/// `PhantomData` items from the derive input quickly becomes a messy
/// syntax-level hack (see PR https://github.com/paritytech/scale-info/pull/31).
/// Instead we take the same approach as `parity-scale-codec` where users are
/// required to explicitly skip fields that cannot be represented in SCALE
/// encoding, using the `#[codec(skip)]` attribute.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(
serialize = "T::Type: Serialize",
deserialize = "T::Type: DeserializeOwned",
))
)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug)]
pub struct TypeDefPhantom<T: Form = MetaForm> {
/// The PhantomData type parameter
#[cfg_attr(feature = "serde", serde(rename = "type"))]
type_param: T::Type,
}

impl IntoPortable for TypeDefPhantom {
type Output = TypeDefPhantom<PortableForm>;

fn into_portable(self, registry: &mut Registry) -> Self::Output {
TypeDefPhantom {
type_param: registry.register_type(&self.type_param),
}
}
}

impl TypeDefPhantom {
/// Creates a new phantom type definition.
pub fn new(type_param: MetaType) -> Self {
Self { type_param }
}
}

impl<T> TypeDefPhantom<T>
where
T: Form,
{
/// Returns the type parameter type of the phantom type.
pub fn type_param(&self) -> &T::Type {
&self.type_param
}
}
26 changes: 25 additions & 1 deletion test_suite/tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
// limitations under the License.
#![cfg_attr(not(feature = "std"), no_std)]

use scale_info::prelude::boxed::Box;
use scale_info::prelude::{
boxed::Box,
marker::PhantomData,
};

use pretty_assertions::assert_eq;
use scale_info::{
Expand Down Expand Up @@ -73,6 +76,27 @@ fn struct_derive() {
assert_type!(SelfTyped, self_typed_type);
}

#[test]
fn phantom_data_is_part_of_the_type_info() {
#[allow(unused)]
#[derive(TypeInfo)]
struct P<T> {
a: u8,
m: PhantomData<T>,
}

let ty = Type::builder()
.path(Path::new("P", "derive"))
.type_params(tuple_meta_type!(bool))
.composite(
Fields::named()
.field_of::<u8>("a", "u8")
.field_of::<PhantomData<bool>>("m", "PhantomData<T>"),
);

assert_type!(P<bool>, ty);
}

#[test]
fn tuple_struct_derive() {
#[allow(unused)]
Expand Down
35 changes: 28 additions & 7 deletions test_suite/tests/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

use scale_info::prelude::{
boxed::Box,
marker::PhantomData,
string::String,
vec,
vec::Vec,
Expand Down Expand Up @@ -133,13 +134,9 @@ fn test_builtins() {
assert_json_for_type::<String>(json!({ "def": { "primitive": "str" } }));
assert_json_for_type::<str>(json!({ "def": { "primitive": "str" } }));
// PhantomData
assert_json_for_type::<core::marker::PhantomData<bool>>(json!({
"path": ["PhantomData"],
"params": [1],
"def": {
"composite": {},
}
}))
assert_json_for_type::<PhantomData<bool>>(
json!({ "def": { "phantom": { "type": 1 } }, }),
)
}

#[test]
Expand Down Expand Up @@ -197,6 +194,30 @@ fn test_struct() {
}));
}

#[test]
fn test_struct_with_phantom() {
use scale_info::prelude::marker::PhantomData;
#[derive(TypeInfo)]
struct Struct<T> {
a: i32,
b: PhantomData<T>,
}

assert_json_for_type::<Struct<u8>>(json!({
"path": ["json", "Struct"],
"params": [1],
"def": {
"composite": {
"fields": [
{ "name": "a", "type": 2, "typeName": "i32" },
// type 1 is the `u8` in the `PhantomData`
{ "name": "b", "type": 3, "typeName": "PhantomData<T>" },
],
},
}
}));
}

#[test]
fn test_clike_enum() {
#[derive(TypeInfo)]
Expand Down