-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
use rustc_abi::FieldIdx; | ||
use rustc_hir::LangItem; | ||
use rustc_middle::mir::interpret::CtfeProvenance; | ||
use rustc_middle::span_bug; | ||
use rustc_middle::ty::layout::TyAndLayout; | ||
use rustc_middle::ty::{self, ScalarInt, Ty}; | ||
use rustc_span::{Symbol, sym}; | ||
|
||
use crate::const_eval::CompileTimeMachine; | ||
use crate::interpret::{ | ||
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok, | ||
}; | ||
|
||
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { | ||
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place. | ||
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 all fields of the `TypeInfo` struct. | ||
for (idx, field) in ty_struct.fields.iter_enumerated() { | ||
let field_dest = self.project_field(dest, idx)?; | ||
let downcast = |name: Symbol| { | ||
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants(); | ||
let variant_id = variants | ||
.iter_enumerated() | ||
.find(|(_idx, var)| var.name == name) | ||
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) | ||
.0; | ||
|
||
interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?)) | ||
}; | ||
match field.name { | ||
sym::kind => { | ||
let variant_index = match ty.kind() { | ||
ty::Tuple(fields) => { | ||
let (variant, variant_place) = downcast(sym::Tuple)?; | ||
// project to the single tuple variant field of `type_info::Tuple` struct type | ||
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 | ||
} | ||
// For now just merge all primitives into one `Leaf` variant with no data | ||
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::Dynamic(..) | ||
| ty::Closure(..) | ||
| ty::CoroutineClosure(..) | ||
| ty::Coroutine(..) | ||
| ty::CoroutineWitness(..) | ||
| ty::Never | ||
| ty::Alias(..) | ||
| ty::Param(_) | ||
| ty::Bound(..) | ||
| ty::Placeholder(_) | ||
| ty::Infer(..) | ||
| ty::Error(_) => downcast(sym::Other)?.0, | ||
}; | ||
self.write_discriminant(variant_index, &field_dest)? | ||
} | ||
sym::size => { | ||
let layout = self.layout_of(ty)?; | ||
let variant_index = if layout.is_sized() { | ||
let (variant, variant_place) = downcast(sym::Some)?; | ||
let size_field_place = | ||
self.project_field(&variant_place, FieldIdx::ZERO)?; | ||
self.write_scalar( | ||
ScalarInt::try_from_target_usize(layout.size.bytes(), self.tcx.tcx) | ||
.unwrap(), | ||
&size_field_place, | ||
)?; | ||
variant | ||
} else { | ||
downcast(sym::None)?.0 | ||
}; | ||
self.write_discriminant(variant_index, &field_dest)?; | ||
} | ||
other => span_bug!(self.tcx.span, "unknown `Type` field {other}"), | ||
} | ||
} | ||
|
||
interp_ok(()) | ||
} | ||
|
||
pub(crate) fn write_tuple_fields( | ||
&mut self, | ||
tuple_place: impl Writeable<'tcx, CtfeProvenance>, | ||
fields: &[Ty<'tcx>], | ||
tuple_ty: Ty<'tcx>, | ||
) -> InterpResult<'tcx> { | ||
// project into the `type_info::Tuple::fields` field | ||
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?; | ||
// get the `type_info::Field` type from `fields: &[Field]` | ||
let field_type = fields_slice_place | ||
.layout() | ||
.ty | ||
.builtin_deref(false) | ||
.unwrap() | ||
.sequence_element_type(self.tcx.tcx); | ||
// Create an array with as many elements as the number of fields in the inspected tuple | ||
let fields_layout = | ||
self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?; | ||
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?; | ||
let mut fields_places = self.project_array_fields(&fields_place)?; | ||
|
||
let tuple_layout = self.layout_of(tuple_ty)?; | ||
|
||
while let Some((i, place)) = fields_places.next(self)? { | ||
let field_ty = fields[i as usize]; | ||
self.write_field(field_ty, place, tuple_layout, i)?; | ||
} | ||
|
||
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable); | ||
|
||
let ptr = Immediate::new_slice(fields_place.ptr(), fields.len() as u64, self); | ||
|
||
self.write_immediate(ptr, &fields_slice_place) | ||
} | ||
|
||
fn write_field( | ||
&mut self, | ||
field_ty: Ty<'tcx>, | ||
place: MPlaceTy<'tcx>, | ||
layout: TyAndLayout<'tcx>, | ||
idx: u64, | ||
) -> InterpResult<'tcx> { | ||
for (field_idx, field_ty_field) in | ||
place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() | ||
{ | ||
let field_place = self.project_field(&place, field_idx)?; | ||
match field_ty_field.name { | ||
sym::ty => self.write_type_id(field_ty, &field_place)?, | ||
sym::offset => { | ||
let offset = layout.fields.offset(idx as usize); | ||
self.write_scalar( | ||
ScalarInt::try_from_target_usize(offset.bytes(), self.tcx.tcx).unwrap(), | ||
&field_place, | ||
)?; | ||
} | ||
other => { | ||
span_bug!(self.tcx.def_span(field_ty_field.did), "unimplemented field {other}") | ||
} | ||
} | ||
} | ||
interp_ok(()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,9 @@ pub use drop_guard::DropGuard; | |
#[doc(inline)] | ||
pub use crate::intrinsics::transmute; | ||
|
||
#[unstable(feature = "type_info", issue = "146922")] | ||
pub mod type_info; | ||
Comment on lines
33
to
+35
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. 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 commentThe 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 |
||
|
||
/// Takes ownership and "forgets" about the value **without running its destructor**. | ||
/// | ||
/// Any resources the value manages, such as heap memory or a file handle, will linger | ||
|
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