Skip to content

Commit eef08a3

Browse files
committed
Give all bytes of TypeId provenance
1 parent ad635e5 commit eef08a3

File tree

5 files changed

+79
-34
lines changed

5 files changed

+79
-34
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
4444
)?;
4545
self.copy_op_allow_transmute(&op, dest)?;
4646

47-
// Give the first pointer-size bytes provenance that knows about the type id.
47+
// Give the each pointer-sized chunk provenance that knows about the type id.
4848
// Here we rely on `TypeId` being a newtype around an array of pointers, so we
49-
// first project to its only field and then the first array element.
49+
// first project to its only field and then the array elements.
5050
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
5151
let first = self.project_field(dest, FieldIdx::ZERO)?;
52-
let first = self.project_index(&first, 0)?;
53-
let offset = self.read_scalar(&first)?.to_target_usize(&tcx)?;
54-
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
55-
let ptr = self.global_root_pointer(ptr)?;
56-
let val = Scalar::from_pointer(ptr, &tcx);
57-
self.write_scalar(val, &first)
52+
let mut elem_iter = self.project_array_fields(&first)?;
53+
while let Some((_, elem)) = elem_iter.next(self)? {
54+
// Decorate this part of the hash with provenance; leave the integer part unchanged.
55+
let hash_fragment = self.read_scalar(&elem)?.to_target_usize(&tcx)?;
56+
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(hash_fragment));
57+
let ptr = self.global_root_pointer(ptr)?;
58+
let val = Scalar::from_pointer(ptr, &tcx);
59+
self.write_scalar(val, &elem)?;
60+
}
61+
interp_ok(())
5862
}
5963

6064
/// Returns `true` if emulation happened.
@@ -101,34 +105,36 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
101105
let mut a_fields = self.project_array_fields(&a_fields)?;
102106
let mut b_fields = self.project_array_fields(&b_fields)?;
103107

104-
let (_idx, a) = a_fields
105-
.next(self)?
106-
.expect("we know the layout of TypeId has at least 2 array elements");
107-
let a = self.deref_pointer(&a)?;
108-
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
109-
110-
let (_idx, b) = b_fields
111-
.next(self)?
112-
.expect("we know the layout of TypeId has at least 2 array elements");
113-
let b = self.deref_pointer(&b)?;
114-
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
108+
let mut provenance_a = None;
109+
let mut provenance_b = None;
110+
let mut provenance_matches = true;
115111

116-
let provenance_matches = a == b;
112+
while let Some((i, a)) = a_fields.next(self)? {
113+
let (_, b) = b_fields.next(self)?.unwrap();
117114

118-
let mut eq_id = offset_a == offset_b;
115+
let a = self.deref_pointer(&a)?;
116+
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
119117

120-
while let Some((_, a)) = a_fields.next(self)? {
121-
let (_, b) = b_fields.next(self)?.unwrap();
118+
let b = self.deref_pointer(&b)?;
119+
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
122120

123-
let a = self.read_target_usize(&a)?;
124-
let b = self.read_target_usize(&b)?;
125-
eq_id &= a == b;
126-
}
121+
if *provenance_a.get_or_insert(a) != a {
122+
throw_ub_format!(
123+
"type_id_eq: the first TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
124+
)
125+
}
126+
if *provenance_b.get_or_insert(b) != b {
127+
throw_ub_format!(
128+
"type_id_eq: the second TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
129+
)
130+
}
131+
provenance_matches &= a == b;
127132

128-
if !eq_id && provenance_matches {
129-
throw_ub_format!(
130-
"type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents"
131-
)
133+
if offset_a != offset_b && provenance_matches {
134+
throw_ub_format!(
135+
"type_id_eq: one of the TypeId arguments is invalid, chunk {i} of the hash does not match the type it represents"
136+
)
137+
}
132138
}
133139

134140
self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;

tests/ui/consts/const_transmute_type_id3.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Test that all bytes of a TypeId must have the
2+
//! TypeId marker provenance.
3+
14
#![feature(const_type_id, const_trait_impl, const_cmp)]
25

36
use std::any::TypeId;
@@ -10,7 +13,7 @@ const _: () = {
1013
std::ptr::write(ptr.offset(1), 999);
1114
}
1215
assert!(a == b);
13-
//~^ ERROR: one of the TypeId arguments is invalid, the hash does not match the type it represents
16+
//~^ ERROR: pointer must point to some allocation
1417
};
1518

1619
fn main() {}

tests/ui/consts/const_transmute_type_id3.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error[E0080]: type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents
2-
--> $DIR/const_transmute_type_id3.rs:12:13
1+
error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got 0x3e7[noalloc] which is a dangling pointer (it has no provenance)
2+
--> $DIR/const_transmute_type_id3.rs:15:13
33
|
44
LL | assert!(a == b);
55
| ^^^^^^ evaluation of `_` failed inside this call
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! Test that we require an equal TypeId to have the same integer
2+
//! part, even if the provenance matches.
3+
4+
#![feature(const_type_id, const_trait_impl, const_cmp)]
5+
6+
use std::any::TypeId;
7+
8+
const _: () = {
9+
let a = TypeId::of::<()>();
10+
let mut b = TypeId::of::<()>();
11+
unsafe {
12+
let ptr = &mut b as *mut TypeId as *mut *const ();
13+
// Copy the ptr at index 0 to index 1
14+
let val = std::ptr::read(ptr);
15+
std::ptr::write(ptr.offset(1), val);
16+
}
17+
assert!(a == b);
18+
//~^ ERROR: type_id_eq: one of the TypeId arguments is invalid, chunk 1 of the hash does not match the type it represents
19+
};
20+
21+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0080]: type_id_eq: one of the TypeId arguments is invalid, chunk 1 of the hash does not match the type it represents
2+
--> $DIR/const_transmute_type_id5.rs:17:13
3+
|
4+
LL | assert!(a == b);
5+
| ^^^^^^ evaluation of `_` failed inside this call
6+
|
7+
note: inside `<TypeId as PartialEq>::eq`
8+
--> $SRC_DIR/core/src/any.rs:LL:COL
9+
note: inside `<TypeId as PartialEq>::eq::compiletime`
10+
--> $SRC_DIR/core/src/any.rs:LL:COL
11+
= note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info)
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)