-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Reflection MVP #146923
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
base: master
Are you sure you want to change the base?
Reflection MVP #146923
Conversation
aab0141
to
4234855
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
4234855
to
6dd9a6a
Compare
This comment has been minimized.
This comment has been minimized.
6dd9a6a
to
a77bbe2
Compare
let op = ecx.project_field(&args[0], FieldIdx::ZERO)?; | ||
let op = ecx.project_index(&op, 0)?; | ||
let id = ecx.read_scalar(&op)?.to_pointer(ecx)?; | ||
let (ty, _offset) = ecx.get_ptr_type_id(id)?; |
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.
Can you use read_type_id instead?
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.
Ah yea, this code predates that
/// Compute the type information of a concrete type. | ||
/// It can only be called at compile time, the backends do | ||
/// not implement it. | ||
#[rustc_intrinsic] |
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.
Typically we put all intrinsics into the intrinsics
module. It's nice to have all these operations in one place since they are language primitives and thus quite different from the rest of the standard library.
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.
Yea, for the single intrinsic scheme I agree, but it does get a bit boilerplaty to keep intrinsics separate from their one single caller if we have a lot of those
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.
Happy to move them if my sentiment is not shared
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.
OTOH it gets tricky to figure out what the language surface provided by the compiler is if it's spread all across libcore...
This comment has been minimized.
This comment has been minimized.
/// It can only be called at compile time. | ||
#[unstable(feature = "type_info", issue = "146922")] | ||
#[rustc_const_unstable(feature = "type_info", issue = "146922")] | ||
pub const fn info(self) -> Type { |
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.
This currently has zero regards for semver, right?
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.
Yea, but it also only supports tuples, which was an explicit choice so we can handle semver related things when we support Adts
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.
Unless you mean the fact that it allows inspecting a generic param and now knowing it's a tuple. This can look through opaque types and stuff
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.
Oh right it also breaks parametricity (though specialization also does that).
Not super relevant right now, but I hope "discuss semver considerations" is a major item somewhere on the agenda for this feature. ;) (The tracking issue is still rather barebones at the moment.)
/// Tuples. | ||
Tuple(Tuple), | ||
/// Primitives | ||
Leaf, |
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 assume, at this early stage, Leaf is mostly a placeholder and will be renamed to something more fitting like Primitive, if not wholly replaced by variants for (kinds of) primitives?
I apologize if, in my ignorance, i'm missing something or jumping way too far ahead
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.
Yea, you're absolutely spot on. I'll add docs for this
#[unstable(feature = "type_info", issue = "146922")] | ||
#[rustc_const_unstable(feature = "type_info", issue = "146922")] | ||
pub const fn of<T: 'static>() -> Self { | ||
TypeId::of::<T>().info() |
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.
TypeId::of::<T>().info() | |
const { TypeId::of::<T>().info() } |
This makes it so that the function is callable at run time.
It seems like... trying to obtain a |
Oh whoops, that shouldn't happen. I'll add some more tests |
library/core/src/mem/type_info.rs
Outdated
/// Primitives | ||
Leaf, | ||
/// FIXME(#146922): add all the common types | ||
Unimplemented, |
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.
See https://docs.rs/const-type-layout/latest/const_type_layout/struct.TypeLayoutInfo.html for some prior work
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This is an MVP. Please do not flood this PR with all your wildest reflection dreams. Anything that suggests to extend the scope of this PR is off-topic. |
Currently, this implementation says that, in the type |
let mut ptr = self.mplace_to_ref(&fields_place)?; | ||
ptr.layout = self.layout_of(Ty::new_imm_ref( | ||
self.tcx.tcx, | ||
self.tcx.lifetimes.re_static, | ||
fields_layout.ty, | ||
))?; | ||
|
||
let slice_type = Ty::new_imm_ref( | ||
self.tcx.tcx, | ||
self.tcx.lifetimes.re_static, | ||
Ty::new_slice(self.tcx.tcx, field_type), | ||
); | ||
let slice_type = self.layout_of(slice_type)?; | ||
self.unsize_into(&ptr.into(), slice_type, &fields_slice_place)?; |
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.
All this here does is construct the wide pointer for the slice, right?
Immediate::new_slice
and ImmTy::from_immediate
should be an easier to do that.
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.
ah oops, good call
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.
newer contributor here. basically, I just got some nits you can ignore :)
thank you so much for working on this!!! :D
|
||
#[unstable(feature = "type_info", issue = "146922")] | ||
pub mod type_info; |
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.
This might be on purpose, but did you intend to use the same re-export pattern as above?
Otherwise, wouldn't this make a guarantee that everything in this module would become unstable/stable at the same time? (and prevent making changes to those internals..?)
if other MVPs tend to ignore this stuff, please ignore this comment :)
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.
if I make the module stable, its internals would not become stable. The module may also stay unstable forever. For easier usage while entirely unstable I'm just making the module publicly available so all internals can be used and played with
ty::Tuple(fields) => { | ||
let (variant, variant_place) = downcast(sym::Tuple)?; | ||
// `Tuple` struct | ||
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?; | ||
assert_eq!( | ||
1, | ||
tuple_place | ||
.layout() | ||
.ty | ||
.ty_adt_def() | ||
.unwrap() | ||
.non_enum_variant() | ||
.fields | ||
.len() | ||
); | ||
self.write_tuple_fields(tuple_place, fields, ty)?; | ||
variant | ||
} | ||
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => { | ||
downcast(sym::Leaf)?.0 | ||
} | ||
ty::Adt(_, _) | ||
| ty::Foreign(_) | ||
| ty::Str | ||
| ty::Array(_, _) | ||
| ty::Pat(_, _) | ||
| ty::Slice(_) | ||
| ty::RawPtr(..) | ||
| ty::Ref(..) | ||
| ty::FnDef(..) | ||
| ty::FnPtr(..) | ||
| ty::UnsafeBinder(..) |
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.
Adding a few comments in this function makes it much easier to edit, imv. Took me a sec to orient myself in here lol
(ignore if you want; mvp)
ty::Tuple(fields) => { | |
let (variant, variant_place) = downcast(sym::Tuple)?; | |
// `Tuple` struct | |
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?; | |
assert_eq!( | |
1, | |
tuple_place | |
.layout() | |
.ty | |
.ty_adt_def() | |
.unwrap() | |
.non_enum_variant() | |
.fields | |
.len() | |
); | |
self.write_tuple_fields(tuple_place, fields, ty)?; | |
variant | |
} | |
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => { | |
downcast(sym::Leaf)?.0 | |
} | |
ty::Adt(_, _) | |
| ty::Foreign(_) | |
| ty::Str | |
| ty::Array(_, _) | |
| ty::Pat(_, _) | |
| ty::Slice(_) | |
| ty::RawPtr(..) | |
| ty::Ref(..) | |
| ty::FnDef(..) | |
| ty::FnPtr(..) | |
| ty::UnsafeBinder(..) | |
ty::Tuple(fields) => { | |
let (variant, variant_place) = downcast(sym::Tuple)?; | |
// create the inner `core::mem::type_info::Tuple` struct | |
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?; | |
assert_eq!( | |
1, | |
tuple_place | |
.layout() | |
.ty | |
.ty_adt_def() | |
.unwrap() | |
.non_enum_variant() | |
.fields | |
.len() | |
); | |
self.write_tuple_fields(tuple_place, fields, ty)?; | |
variant | |
} | |
// write primitives directly (not much info to get) | |
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => { | |
downcast(sym::Leaf)?.0 | |
} | |
// other types are currently unimplemented. | |
// | |
// they'll result in `TypeKind::Unimplemented` | |
ty::Adt(_, _) | |
| ty::Foreign(_) | |
| ty::Str | |
| ty::Array(_, _) | |
| ty::Pat(_, _) | |
| ty::Slice(_) | |
| ty::RawPtr(..) | |
| ty::Ref(..) | |
| ty::FnDef(..) | |
| ty::FnPtr(..) | |
| ty::UnsafeBinder(..) |
pub(crate) fn write_type_info( | ||
&mut self, | ||
ty: Ty<'tcx>, | ||
dest: &impl Writeable<'tcx, CtfeProvenance>, | ||
) -> InterpResult<'tcx> { | ||
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span); | ||
let ty_struct = self.tcx.type_of(ty_struct).instantiate_identity(); | ||
assert_eq!(ty_struct, dest.layout().ty); | ||
let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant(); | ||
for (idx, field) in ty_struct.fields.iter_enumerated() { | ||
let field_dest = self.project_field(dest, idx)?; | ||
let downcast = |name: Symbol| { |
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.
a couple comments to help direct contributors (ignore if you want; MVP)
pub(crate) fn write_type_info( | |
&mut self, | |
ty: Ty<'tcx>, | |
dest: &impl Writeable<'tcx, CtfeProvenance>, | |
) -> InterpResult<'tcx> { | |
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span); | |
let ty_struct = self.tcx.type_of(ty_struct).instantiate_identity(); | |
assert_eq!(ty_struct, dest.layout().ty); | |
let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant(); | |
for (idx, field) in ty_struct.fields.iter_enumerated() { | |
let field_dest = self.project_field(dest, idx)?; | |
let downcast = |name: Symbol| { | |
/// Creates a `core::mem::type_info::TypeInfo` for a given type, `ty`. | |
pub(crate) fn write_type_info( | |
&mut self, | |
ty: Ty<'tcx>, | |
dest: &impl Writeable<'tcx, CtfeProvenance>, | |
) -> InterpResult<'tcx> { | |
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span); | |
let ty_struct = self.tcx.type_of(ty_struct).instantiate_identity(); | |
assert_eq!(ty_struct, dest.layout().ty); | |
let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant(); | |
// fill each field of `TypeInfo` | |
for (idx, field) in ty_struct.fields.iter_enumerated() { | |
let field_dest = self.project_field(dest, idx)?; | |
let downcast = |name: Symbol| { |
☔ The latest upstream changes (presumably #147128) made this pull request unmergeable. Please resolve the merge conflicts. |
|
a77bbe2
to
4d1d728
Compare
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
yea, just like any other value until we get runtime-sized types. You just can't look through further TypeIds that are contained within them |
4d1d728
to
c1926e6
Compare
This comment has been minimized.
This comment has been minimized.
c1926e6
to
5155c5f
Compare
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
I am opening this PR for discussion about the general design we should start out with, as there are various options (that are not too hard to transition between each other, so we should totally just pick one and go with it and reiterate later)
r? @scottmcm and @joshtriplett
project goal issue: rust-lang/rust-project-goals#406
tracking issue: #146922
The design currently implemented by this PR is
TypeId::info
(method, usually used asid.info()
returns aType
structType
struct has fields that contain information about the typekind
, which is a non-exhaustive enum over all possible type kinds and their specific information. So it has aTuple(Tuple)
variant, where the only field is aTuple
struct type that contains more information (The list of type ids that make up the tuple).TypeId::info
again.TypeId
toType
, and it does all the workAn alternative design could be
TypeId
that return all the individual information pieces (size, align, number of fields, number of variants, ...)TypeId::info
is likely expensive and wasteful)Option
return types, or "general" methods likenum_fields
returning 0 for primitives) instead of matching and field accessesTypeId::info
in terms of this designThe backing implementation is modular enough that switching from one to the other is probably not an issue, and the alternative design could be easier for the CTFE engine's implementation, just not as nice to use for end users (without crates wrapping the logic)
One wart of this design that I'm fixing in separate branches is that
TypeId::info
will panic if used at runtime, while it should be uncallable