Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion quest/m3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ generic std gap is planned here even when moq never calls it.
- [The rest of a vector's methods](/quest/m3/ts-vec-rest.md) - `try_reserve`, a deque's rotations and searches, and `Vec::splice` translate
- [Type errors at the Rust that made them](/quest/m3/ts-span-precision.md) - a `tsc` error on output names the tail expression or call argument that made it
- [Fresh writes beside a zipped iterator that points at a part](/quest/m3/ts-fresh-shapes.md) - zipping slots with a `Vec<[T; N]>`, `vec![row; n]`, or `copied` of what the slot holds translates
- [Fresh writes through a vector's own accessors](/quest/m3/ts-fresh-vec-access.md) - a whole write into a vector's element stays fresh through a shared borrow, `first_mut`, a by-value array `for`, and a byte-holding collect
- [Python's wake on the loop's own thread](/quest/m3/ffi-python-wake.md) - a wake on the event loop's thread resumes the future with no socket pair
- [Python's call cost](/quest/m3/ffi-python-calls.md) - a call on an object costs what BoltFFI's does, or the gap is explained
- [A std map with a crate's own BuildHasher](/quest/m3/ts-map-own-hasher.md) - std's and hashbrown's maps built with a crate's own hasher translate for keys that are JS objects
4 changes: 0 additions & 4 deletions quest/m3/ts-fresh-shapes.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,3 @@ Recommend the first if it stays sound under specialization; it covers every adap
once.

Public API: the translator's output.

## Related

- [Fresh writes through a vector's own accessors](/quest/m3/ts-fresh-vec-access.md) - the other refusals found beside this one
32 changes: 0 additions & 32 deletions quest/m3/ts-fresh-vec-access.md

This file was deleted.

10 changes: 5 additions & 5 deletions ts/fixtures/lang/lengths/expected/lengths.Row.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import { copy$tuple2_u16_u16 } from "./lib.ts";
import { copy$tuple2_u8_u8 } from "./lib.ts";
import * as Loud from "./lengths.Loud.ts";

export function clone$Clone_lengths_Loud(self: Row<Loud.Loud>): Row<Loud.Loud> {
return new Row(Loud.clone$Clone(self._0), copy$tuple2_u16_u16(self._1));
return new Row(Loud.clone$Clone(self._0), copy$tuple2_u8_u8(self._1));
}

/** A value and a pair of tags kept as an object, of no byte held in place. */
/** A value and a pair of tags kept as an object. */
export class Row<T> {
_0: T;
_1: [rt.u16, rt.u16];
_1: [rt.u8, rt.u8];

constructor(_0: T, _1: [rt.u16, rt.u16]) {
constructor(_0: T, _1: [rt.u8, rt.u8]) {
this._0 = _0;
this._1 = _1;
}
Expand Down
254 changes: 142 additions & 112 deletions ts/fixtures/lang/lengths/expected/lengths.ts

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions ts/fixtures/lang/lengths/expected/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ export function copy$array_array3_u8(v: Uint8Array[]): Uint8Array[] {
return v.map(copy$array_u8);
}

export function copy$array_lengths_Row_lengths_Loud(v: Row.Row<Loud.Loud>[]): Row.Row<Loud.Loud>[] {
return v.map(copy$lengths_Row_lengths_Loud);
}

export function copy$array_lengths_Row_u8(v: Row.Row<rt.u8>[]): Row.Row<rt.u8>[] {
return v.map(copy$lengths_Row_u8);
}

export function copy$array_lengths_Tagged_u32(v: Tagged.Tagged<rt.u32>[]): Tagged.Tagged<rt.u32>[] {
return v.slice();
}
Expand All @@ -67,11 +75,11 @@ export function copy$array_u8(v: Uint8Array): Uint8Array {
}

export function copy$lengths_Row_lengths_Loud(v: Row.Row<Loud.Loud>): Row.Row<Loud.Loud> {
return new Row.Row(v._0, copy$tuple2_u16_u16(v._1));
return new Row.Row(v._0, copy$tuple2_u8_u8(v._1));
}

export function copy$lengths_Row_u8(v: Row.Row<rt.u8>): Row.Row<rt.u8> {
return new Row.Row(v._0, copy$tuple2_u16_u16(v._1));
return new Row.Row(v._0, copy$tuple2_u8_u8(v._1));
}

export function copy$lengths_Tagged_array0_u32(v: Tagged.Tagged<rt.u32[]>): Tagged.Tagged<rt.u32[]> {
Expand Down Expand Up @@ -274,7 +282,7 @@ export function copy$mem_MaybeUninit_tuple2_u8_u8(v: [rt.u8, rt.u8]): [rt.u8, rt
return [v[0], v[1]];
}

export function copy$tuple2_u16_u16(v: [rt.u16, rt.u16]): [rt.u16, rt.u16] {
export function copy$tuple2_u8_u8(v: [rt.u8, rt.u8]): [rt.u8, rt.u8] {
return [v[0], v[1]];
}

Expand Down
41 changes: 21 additions & 20 deletions ts/fixtures/lang/lengths/src/lengths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//! arrays of bytes, of a type parameter's values, and of arrays, each at a length of none,
//! one, and more. Their writes are judged in the bounds of the body making them, as
//! filling memory nothing points into yet or giving what they replace another object,
//! through what std's iterators give back and what `copied` and `vec![x; n]` make of a
//! `Copy` type whose `Clone` logs and panics.
//! through what std's iterators and a vector's accessors give back and what `copied` and
//! `vec![x; n]` make of a `Copy` type whose `Clone` logs and panics.

use std::cell::RefCell;
use std::mem::MaybeUninit;
Expand Down Expand Up @@ -210,40 +210,41 @@ impl Put for Loud {
}
}

/// A value and a pair of tags kept as an object, of no byte held in place.
/// A value and a pair of tags kept as an object.
#[derive(Clone, Copy)]
struct Row<T>(T, (u16, u16));
struct Row<T>(T, (u8, u8));

/// The first row of a vector of `$len` written whole and its tags changed through a pointer
/// to them, the second's changed in place, and each row put.
/// A vector's length, its first row written whole and its tags changed through a pointer
/// to them, the second's changed in place, and each row put as the vector is moved out.
macro_rules! rewritten {
($rows:ident, $len:expr, $x:expr, $out:ident) => {
if $len > 0 {
$rows[0] = Row($x, (0x3031, 0x3233));
let tags = &raw mut $rows[0].1;
unsafe { (*tags).0 ^= 0x1010 }
($rows:ident, $x:expr, $out:ident) => {
$out.push($rows.len() as u8);
if let Some(first) = $rows.first_mut() {
*first = Row($x, (0x30, 0x31));
let tags = &raw mut first.1;
unsafe { (*tags).0 ^= 0x10 }
}
if $len > 1 {
$rows[1].1.1 ^= 0x2020;
if let Some(Row(_, (_, b))) = $rows.get_mut(1) {
*b ^= 0x20;
}
for Row(value, (a, b)) in $rows {
value.put($out);
$out.extend(a.to_le_bytes().into_iter().chain(b.to_le_bytes()));
$out.extend([a, b]);
}
};
}

/// [`repeated`]'s body, over `$x`'s type.
macro_rules! repeated {
($data:ident, $x:expr, $y:expr, $out:ident) => {{
let rows = [Row($y, (0x0102, 0x0304)), Row($x, (0x0506, 0x0708))];
let rows = [Row($y, (0x01, 0x02)), Row($x, (0x03, 0x04))];
let mut copies: Vec<_> = rows.iter().copied().collect();
rewritten!(copies, 2, $x, $out);
let mut repeats = vec![Row($x, (0x090a, 0x0b0c)); 1 + $data.len() % 3];
rewritten!(repeats, 1 + $data.len() % 3, $x, $out);
for Row(value, (a, b)) in &rows {
rewritten!(copies, $x, $out);
let mut repeats = vec![Row($x, (0x05, 0x06)); 1 + $data.len() % 3];
rewritten!(repeats, $x, $out);
for Row(value, (a, b)) in rows {
value.put($out);
$out.extend(a.to_le_bytes().into_iter().chain(b.to_le_bytes()));
$out.extend([a, b]);
}
}};
}
Expand Down
2 changes: 2 additions & 0 deletions ts/fixtures/reject/census.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ fixtures/reject/src/kept.rs:lent_kept+4:46: a raw pointer to `std::cell::UnsafeC
fixtures/reject/src/kept.rs:lent_kept+6:22: `std::iter::once` with a mutable reference to a value JS copies, which its mapping would copy
fixtures/reject/src/kept.rs:mapped_kept+4:39: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Mapped`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:pushed_part_kept+4:22: a raw pointer to `(u16, u16)` in `kept::Pushed`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:reaimed+2:13: a raw pointer to `(u16, u16)` in `kept::Reaimed`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:ref_replaced+2:32: a raw pointer to `kept::ByRefReplace` in `(u16, kept::ByRefReplace)`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:ref_taken+2:32: a raw pointer to `kept::ByRefTake` in `(u16, kept::ByRefTake)`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:reversed+3:13: a raw pointer to `(u16, u16)` in `kept::Reversed`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:uninit_rewritten+3:47: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Rewritten`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:written_through_alias+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Aliased`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:written_through_loaded+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Loaded`, which the crate may put another object in place of
Expand Down
2 changes: 2 additions & 0 deletions ts/fixtures/reject/expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ fixtures/reject/src/kept.rs:lent_kept+4:46: a raw pointer to `std::cell::UnsafeC
fixtures/reject/src/kept.rs:lent_kept+6:22: `std::iter::once` with a mutable reference to a value JS copies, which its mapping would copy
fixtures/reject/src/kept.rs:mapped_kept+4:39: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Mapped`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:pushed_part_kept+4:22: a raw pointer to `(u16, u16)` in `kept::Pushed`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:reaimed+2:13: a raw pointer to `(u16, u16)` in `kept::Reaimed`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:ref_replaced+2:32: a raw pointer to `kept::ByRefReplace` in `(u16, kept::ByRefReplace)`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:ref_taken+2:32: a raw pointer to `kept::ByRefTake` in `(u16, kept::ByRefTake)`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:reversed+3:13: a raw pointer to `(u16, u16)` in `kept::Reversed`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:uninit_rewritten+3:47: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Rewritten`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:written_through_alias+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Aliased`, which the crate may put another object in place of
fixtures/reject/src/kept.rs:written_through_loaded+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Loaded`, which the crate may put another object in place of
Expand Down
26 changes: 26 additions & 0 deletions ts/fixtures/reject/src/kept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,3 +432,29 @@ pub fn key_taken(n: u16) -> u16 {
drop(KEY_TAKE.take());
unsafe { *part }
}

pub struct Reaimed(pub (u16, u16));

/// A fresh allocation written whole through a pointer that a shared borrow of the
/// `UnsafeCell` holding it aimed at another value, into which a pointer may still be read.
pub fn reaimed(n: u16) -> u16 {
let mut other = Reaimed((n, n));
let part = &raw mut other.0;
let at = std::cell::UnsafeCell::new(Box::into_raw(Box::new(Reaimed((0, 0)))));
let shared = &at;
unsafe { *shared.get() = &raw mut other };
unsafe { *at.into_inner() = Reaimed((1, 1)) };
unsafe { (*part).0 }
}

pub struct Reversed(pub (u16, u16));

/// A value std's `Reverse` holds, swapped whole for another while a pointer into it may
/// still be read.
pub fn reversed(n: u16) -> u16 {
let mut slot = std::cmp::Reverse(Reversed((n, n)));
let mut other = std::cmp::Reverse(Reversed((0, 0)));
let part = &raw mut slot.0.0;
std::mem::swap(&mut slot, &mut other);
unsafe { (*part).0 }
}
2 changes: 2 additions & 0 deletions ts/fixtures/reject/ts.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ roots = [
"kept::lent_kept",
"kept::mapped_kept",
"kept::pushed_part_kept",
"kept::reaimed",
"kept::ref_replaced",
"kept::ref_taken",
"kept::reversed",
"kept::uninit_rewritten",
"kept::written_through_alias",
"kept::written_through_loaded",
Expand Down
5 changes: 3 additions & 2 deletions ts/src/lower/copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -980,12 +980,13 @@ impl<'tcx> BodyWrites<'_, '_, '_, 'tcx> {
}

/// Whether the call at `at` given `arg` as a `ty`, a mutable reference or pointer to a
/// type that may be kept as an object, fills memory nothing points into yet there.
/// type that holds by value one that may be kept as an object, as an array's iterator
/// does, fills memory nothing points into yet there.
fn fills_fresh(&mut self, ty: Ty<'tcx>, arg: &mir::Operand<'tcx>, at: Location, made: Option<Local>) -> bool {
let (ty::Ref(_, inner, ty::Mutability::Mut) | ty::RawPtr(inner, ty::Mutability::Mut)) = *ty.kind() else {
return false;
};
self.inline(inner)
!self.parts(inner).is_empty()
&& arg
.place()
.and_then(|p| p.as_local())
Expand Down
42 changes: 41 additions & 1 deletion ts/src/lower/mir/holders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,9 @@ impl<'x, 'a, 'tcx> Trace<'x, 'a, 'tcx> {
}

/// Whether a value of `ty` may hold a pointer, or one behind it, to a value `to` holds
/// for, as what it points at is peeled; or one its type doesn't tell.
/// for, as what it points at is peeled; or one its type doesn't tell. A vector's buffer
/// is its own allocation, never a part of another value, so only what its elements hold
/// may point at one.
fn points_to(&self, ty: Ty<'tcx>, to: impl Fn(Ty<'tcx>) -> bool) -> bool {
let mut seen = FxHashSet::default();
let mut stack = vec![ty];
Expand All @@ -733,6 +735,11 @@ impl<'x, 'a, 'tcx> Trace<'x, 'a, 'tcx> {
}
stack.push(pointee);
}
ty::Adt(adt, args)
if crate::lower::library::std_path(self.t.tcx, adt.did()) == ["raw_vec", "RawVec"] =>
{
stack.extend(args.types())
}
_ => match self.fields(ty) {
Some(fields) => stack.extend(fields),
None => return true,
Expand All @@ -742,6 +749,36 @@ impl<'x, 'a, 'tcx> Trace<'x, 'a, 'tcx> {
false
}

/// Whether code given a shared reference to a value of `ty` may change what it holds or
/// reaches: through an `UnsafeCell` in a field, behind a pointer, or in a type argument,
/// as a hash map's buffer holds its entries; or in one its type doesn't tell. A type
/// parameter's value holds none the body can read a pointer out of but through the
/// crate's code, which [`Self::origins`] follows on its own.
fn mutable_through_shared(&self, ty: Ty<'tcx>) -> bool {
let tcx = self.t.tcx;
let mut seen = FxHashSet::default();
let mut stack = vec![ty];
while let Some(ty) = stack.pop() {
if !seen.insert(ty) {
continue;
}
match ty.kind() {
ty::Adt(adt, _) if tcx.is_lang_item(adt.did(), rustc_attr_ir::LangItem::UnsafeCell) => return true,
ty::Adt(_, args) => stack.extend(args.types()),
ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => {
stack.push(*pointee);
continue;
}
_ => {}
}
match self.fields(ty) {
Some(fields) => stack.extend(fields),
None => return true,
}
}
false
}

/// Whether a value of `ty` may be or hold a `filled` value in place.
fn holds_in_place(&self, ty: Ty<'tcx>, filled: Ty<'tcx>) -> bool {
let tcx = self.t.tcx;
Expand Down Expand Up @@ -1023,6 +1060,9 @@ impl<'x, 'a, 'tcx> Trace<'x, 'a, 'tcx> {
{
defs[borrowed].lent.push(to)
}
// A shared borrow changes nothing but through an `UnsafeCell`.
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place)
if !self.mutable_through_shared(place.ty(&mir.local_decls, self.t.tcx).ty) => {}
Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) if !place.is_indirect() => {
defs[place.local].other = true
}
Expand Down