-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API: Add sem::TyKind::implements_trait
(Draft)
#340
base: master
Are you sure you want to change the base?
Changes from all commits
0ae25af
c3fa8b6
9c7e1c6
1f872f3
2f2b157
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,11 @@ pub use sequence_ty::*; | |
pub use trait_ty::*; | ||
pub use user_ty::*; | ||
|
||
use crate::common::DriverTyId; | ||
use crate::{ | ||
common::{DriverTyId, TyDefId}, | ||
context::with_cx, | ||
ffi::FfiSlice, | ||
}; | ||
use std::{fmt::Debug, marker::PhantomData}; | ||
|
||
/// The semantic representation of a type. | ||
|
@@ -113,7 +117,52 @@ impl<'ast> TyKind<'ast> { | |
} | ||
ty | ||
} | ||
|
||
/// This function tests if a given type implements a trait, specified by | ||
/// the given [`TestTraitRef`]. A [`TraitTestMode`] can be specified as | ||
/// part of the trait reference. | ||
#[must_use] | ||
pub fn implements_trait(self, trait_ref: &TestTraitRef) -> bool { | ||
let check_with_ids = |ids: &[TyDefId]| { | ||
let ffi = FfiTestTraitRef { | ||
trait_ids: ids.into(), | ||
mode: trait_ref.mode, | ||
}; | ||
with_cx(&self, |cx| cx.ty_implements_trait(self, &ffi)) | ||
}; | ||
|
||
match &trait_ref.trait_ref { | ||
TyDefIdSource::Path(path) => check_with_ids(with_cx(&self, |cx| cx.resolve_ty_ids(path))), | ||
TyDefIdSource::Id(id) => check_with_ids(&[*id]), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(feature = "driver-api")] | ||
impl<'ast> TyKind<'ast> { | ||
impl_ty_kind_fn!(data() -> &CommonTyData<'ast>); | ||
} | ||
|
||
macro_rules! impl_ty_kind_fn { | ||
($method:ident () -> $return_ty:ty) => { | ||
impl_ty_kind_fn!($method() -> $return_ty, | ||
Bool, Num, Text, Never, | ||
Tuple, Array, Slice, | ||
Fn, Closure, | ||
Ref, RawPtr, FnPtr, | ||
TraitObj, Adt, Generic, Alias, | ||
Unstable | ||
); | ||
}; | ||
($method:ident () -> $return_ty:ty $(, $item:ident)+) => { | ||
pub fn $method(&self) -> $return_ty { | ||
match self { | ||
$(TyKind::$item(data) => data.$method(),)* | ||
} | ||
} | ||
}; | ||
} | ||
use impl_ty_kind_fn; | ||
|
||
#[repr(C)] | ||
#[cfg_attr(feature = "driver-api", visibility::make(pub))] | ||
|
@@ -139,7 +188,7 @@ impl<'ast> CommonTyData<'ast> { | |
|
||
macro_rules! impl_ty_data { | ||
($self_ty:ty, $enum_name:ident) => { | ||
#[cfg(feature = "driver_api")] | ||
#[cfg(feature = "driver-api")] | ||
impl<'ast> $self_ty { | ||
pub fn data(&self) -> &$crate::sem::ty::CommonTyData<'ast> { | ||
&self.data | ||
|
@@ -154,3 +203,183 @@ macro_rules! impl_ty_data { | |
}; | ||
} | ||
use impl_ty_data; | ||
|
||
/// This struct describes a trait and its generic arguments. It is used to check | ||
/// if a semantic type implements a trait. | ||
/// | ||
/// See [`TyKind::implements_trait`]. | ||
#[derive(Debug)] | ||
pub struct TestTraitRef { | ||
trait_ref: TyDefIdSource, | ||
// TODO generics | ||
mode: TraitTestMode, | ||
} | ||
|
||
impl TestTraitRef { | ||
pub fn builder() -> TestTraitRefBuilder { | ||
TestTraitRefBuilder::new() | ||
} | ||
} | ||
|
||
/// A builder used to construct a [`TestTraitRef`] instance. | ||
#[derive(Debug)] | ||
pub struct TestTraitRefBuilder { | ||
trait_ref: Option<TyDefIdSource>, | ||
mode: TraitTestMode, | ||
} | ||
|
||
impl TestTraitRefBuilder { | ||
fn new() -> Self { | ||
Self { | ||
trait_ref: None, | ||
mode: TraitTestMode::default(), | ||
} | ||
} | ||
|
||
pub fn trait_from_path(&mut self, path: impl Into<String>) -> &mut Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in general we should avoid allocations especially in read-only APIs which is basically what all APIs are in marker_api. 99% of the time the trait name will be a impl Into<Cow<('static|'_), str>> Although |
||
self.trait_ref = Some(TyDefIdSource::Path(path.into())); | ||
self | ||
} | ||
|
||
pub fn trait_from_id(&mut self, id: impl Into<TyDefId>) -> &mut Self { | ||
self.trait_ref = Some(TyDefIdSource::Id(id.into())); | ||
self | ||
} | ||
|
||
pub fn mode(&mut self, mode: TraitTestMode) -> &mut Self { | ||
self.mode = mode; | ||
self | ||
} | ||
|
||
pub fn build(&mut self) -> TestTraitRef { | ||
TestTraitRef { | ||
trait_ref: self.trait_ref.take().expect("the trait was never set"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think With buildstructor you could even achieve the API like this: ctx.test_trait()
.arg_foo(...)
.arg_bar(...)
.call() In this case you won't even need any owned value as an |
||
mode: self.mode, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
#[cfg_attr(feature = "driver-api", visibility::make(pub))] | ||
pub(crate) struct FfiTestTraitRef<'a> { | ||
trait_ids: FfiSlice<'a, TyDefId>, | ||
// TODO generics | ||
mode: TraitTestMode, | ||
} | ||
|
||
#[cfg(feature = "driver-api")] | ||
impl<'a> FfiTestTraitRef<'a> { | ||
pub fn trait_ids(&self) -> &'a [TyDefId] { | ||
self.trait_ids.get() | ||
} | ||
|
||
pub fn mode(&self) -> TraitTestMode { | ||
self.mode | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
#[non_exhaustive] | ||
pub enum TyDefIdSource { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be |
||
Id(TyDefId), | ||
// TODO: Handle the path properly, since `Str` is not ABI safe | ||
Path(String), | ||
} | ||
|
||
impl From<TyDefId> for TyDefIdSource { | ||
fn from(value: TyDefId) -> Self { | ||
TyDefIdSource::Id(value) | ||
} | ||
} | ||
|
||
impl From<String> for TyDefIdSource { | ||
fn from(value: String) -> Self { | ||
TyDefIdSource::Path(value) | ||
} | ||
} | ||
|
||
/// This enum defines how strict the [`TyKind::implements_trait`] test should be. | ||
/// The difference is probably best illustrated by examples. For that, let's | ||
/// take the following traits: | ||
/// | ||
/// ``` | ||
/// // Definitions | ||
/// trait SimpleTrait { /* ... */ } | ||
/// trait GenericTrait<T> { /* ... */ } | ||
/// trait TwoGenericTrait<T, U=u8> { /* ... */ } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is also a case for const generics in traits, although I that's a rare case and we can implement support for that later |
||
/// ``` | ||
/// | ||
/// Now we have a `SimpleType` which implements a few traits: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe just squash this into one code snippet, and make this a line comment to DRY-up code snippet a bit |
||
/// | ||
/// ``` | ||
/// # trait SimpleTrait { /* ... */ } | ||
/// # trait GenericTrait<T> { /* ... */ } | ||
/// # trait TwoGenericTrait<T, U=u8> { /* ... */ } | ||
/// struct SimpleType; | ||
/// | ||
/// impl SimpleTrait for SimpleType {} | ||
/// impl GenericTrait<i16> for SimpleType {} | ||
/// // The second generic argument, defaults to `u8`. | ||
/// impl TwoGenericTrait<u16> for SimpleType {} | ||
/// ``` | ||
/// | ||
/// | Semantic type and checked trait | Lossy | Strict | | ||
/// | --------------------------------------------------------- | ----- | ------ | | ||
/// | `SimpleType` implements `SimpleTrait` | ✅ | ✅ | | ||
/// | `SimpleType` implements `GenericTrait` | ✅ | ❌ | | ||
/// | `SimpleType` implements `GenericTrait<!>` | ❌ | ❌ | | ||
/// | `SimpleType` implements `TwoGenericTrait<u16>` | ✅ | ❌ | | ||
/// | `SimpleType` implements `TwoGenericTrait<u16, u8>` | ✅ | ✅ | | ||
/// | `SimpleType` implements `TwoGenericTrait<u16, !>` | ❌ | ❌ | | ||
/// | ||
/// We can also check traits for types with generic parameter: | ||
/// | ||
/// ``` | ||
/// # trait SimpleTrait { /* ... */ } | ||
/// # trait GenericTrait<T> { /* ... */ } | ||
/// # trait TwoGenericTrait<T, U=u8> { /* ... */ } | ||
/// struct GenericType<T>(T); | ||
/// | ||
/// impl<T> SimpleTrait for GenericType<T> {} | ||
/// impl GenericTrait<u32> for GenericType<u32> {} | ||
/// impl TwoGenericTrait<i32, i32> for GenericType<u32> {} | ||
/// ``` | ||
/// | Semantic type and checked trait | Lossy | Strict | | ||
/// | --------------------------------------------------------- | ----- | ------ | | ||
/// | `GenericType<()>` implements `SimpleTrait` | ✅ | ✅ | | ||
/// | `GenericType<!>` implements `GenericTrait<u32>` | ❌ | ❌ | | ||
/// | `GenericType<u32>` implements `GenericTrait<u32>` | ✅ | ✅ | | ||
/// | `GenericType<u32>` implements `TwoGenericTrait` | ✅ | ❌ | | ||
/// | `GenericType<u32>` implements `TwoGenericTrait<i32>` | ✅ | ❌ | | ||
/// | `GenericType<u32>` implements `TwoGenericTrait<i32, i32>` | ✅ | ✅ | | ||
#[non_exhaustive] | ||
#[derive(Debug, Copy, Clone, Default)] | ||
pub enum TraitTestMode { | ||
/// The comparison will enforce the given generic arguments and allow any | ||
/// additional ones to be freely matched. This is useful for traits with | ||
/// default types for generic parameters. | ||
/// | ||
/// See the documentation of [`TraitTestMode`] for a comparison of the | ||
/// different test modes. | ||
#[default] | ||
Lossy, | ||
/// The comparison will check that the correct number of generics has been | ||
/// provided and will check that all of them match. | ||
/// | ||
/// See the documentation of [`TraitTestMode`] for a comparison of the | ||
/// different test modes. | ||
Strict, | ||
} | ||
|
||
pub struct UserDefinedGeneric<'a> { | ||
_lifetime: PhantomData<&'a ()>, | ||
} | ||
|
||
#[derive(Debug)] | ||
#[non_exhaustive] | ||
#[cfg_attr(feature = "driver-api", visibility::make(pub))] | ||
enum UserDefinedGenericInner<'a> { | ||
ApiType(TyKind<'a>), | ||
// TODO: Handle the path properly, since `Str` is not ABI safe | ||
Path(String), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why this changed. I'd expect such breaking change only during the nightly toochain version update.