The JIT that runs makepad apps from source: lexer, front end, type checker, lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and warm/cold caches keyed by compiler stamp, provided crates bound late, startup images, live patching. User-facing name is `makepad run` (binary `makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal library name. `makepad check` reports rustc-style diagnostics with rustc error codes and checks a whole workspace (`--workspace`). Runs on macOS, Linux, Windows and Android. makepad-remote: one tool schema for apps and the WM (app/1): grabs, captures, traces, frame profiles and tweaks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
129 lines
4.1 KiB
Rust
129 lines
4.1 KiB
Rust
// A field of a value expression (`mk().0`, `three().1.0`) is a place in ONE temporary of the
|
|
// enclosing statement (rustc as_place.rs expr_as_place: the base becomes a temporary, the
|
|
// projection a place in it; a match / if-let scrutinee is no terminating scope). What is moved
|
|
// out of that place is a move path of the temporary: the temporary's drop at the statement's end
|
|
// is elaborated to drop only what remains, fields in declaration order (rustc
|
|
// elaborate_drop.rs open_drop_for_adt / drop_ladder). Relax dropped the moved value of
|
|
// `match mk().0 { Ok(d) => d, .. }` at the statement's end and again with `d` (found by the
|
|
// gpuinit lane: it freed a Wayland connection), and dropped all of `two()` at the end of
|
|
// `let a = match two().0 { d => d };`. Expected outputs are rustc's (2021 and 2024 editions).
|
|
use std::cell::RefCell;
|
|
|
|
thread_local! { static LOG: RefCell<Vec<String>> = RefCell::new(Vec::new()); }
|
|
fn log(s: String) { LOG.with(|l| l.borrow_mut().push(s)); }
|
|
fn take() -> Vec<String> { LOG.with(|l| std::mem::take(&mut *l.borrow_mut())) }
|
|
fn v(s: &[&str]) -> Vec<String> { s.iter().map(|x| x.to_string()).collect() }
|
|
|
|
struct D(&'static str);
|
|
impl Drop for D { fn drop(&mut self) { log(format!("drop {}", self.0)); } }
|
|
struct W(Result<D, ()>);
|
|
fn mk() -> W { W(Ok(D("x"))) }
|
|
|
|
struct Two(D, D);
|
|
fn two() -> Two { Two(D("a"), D("b")) }
|
|
|
|
struct Three(D, Two, D);
|
|
fn three() -> Three { Three(D("p"), two(), D("q")) }
|
|
|
|
struct P(Two, D);
|
|
fn mkp() -> P { P(two(), D("c")) }
|
|
|
|
#[test]
|
|
fn match_scrutinee_field_moved_out_of_temporary_drops_once() {
|
|
{
|
|
let d = match mk().0 { Ok(d) => d, Err(_) => panic!() };
|
|
log(format!("got {}", d.0));
|
|
}
|
|
assert_eq!(take(), v(&["got x", "drop x"]));
|
|
}
|
|
|
|
#[test]
|
|
fn match_scrutinee_partial_move_drops_remaining_field_at_statement_end() {
|
|
{
|
|
let a = match two().0 { d => d };
|
|
log(format!("got {}", a.0));
|
|
}
|
|
// the temporary's unmoved field `b` drops at the end of the `let` statement, `a` at scope end
|
|
assert_eq!(take(), v(&["drop b", "got a", "drop a"]));
|
|
}
|
|
|
|
#[test]
|
|
fn if_let_scrutinee_field_partial_move() {
|
|
{
|
|
if let Two(a, _) = mkp().0 {
|
|
log(format!("got {}", a.0));
|
|
}
|
|
log("after".to_string());
|
|
}
|
|
assert_eq!(take(), v(&["got a", "drop a", "drop b", "drop c", "after"]));
|
|
}
|
|
|
|
#[test]
|
|
fn if_let_scrutinee_enum_field_moved_drops_once() {
|
|
{
|
|
if let Ok(d) = mk().0 {
|
|
log(format!("got {}", d.0));
|
|
}
|
|
log("after".to_string());
|
|
}
|
|
assert_eq!(take(), v(&["got x", "drop x", "after"]));
|
|
}
|
|
|
|
#[test]
|
|
fn let_field_moved_out_of_temporary() {
|
|
{
|
|
let a = two().0;
|
|
log(format!("got {}", a.0));
|
|
}
|
|
assert_eq!(take(), v(&["drop b", "got a", "drop a"]));
|
|
}
|
|
|
|
#[test]
|
|
fn let_nested_field_moved_out_of_temporary() {
|
|
{
|
|
let a = three().1.0;
|
|
log(format!("got {}", a.0));
|
|
}
|
|
// one temporary, `three()`: its remaining parts in declaration order
|
|
assert_eq!(take(), v(&["drop p", "drop b", "drop q", "got a", "drop a"]));
|
|
}
|
|
|
|
#[test]
|
|
fn match_nested_field_scrutinee_partial_move() {
|
|
{
|
|
let a = match three().1.0 { d => d };
|
|
log(format!("got {}", a.0));
|
|
}
|
|
assert_eq!(take(), v(&["drop p", "drop b", "drop q", "got a", "drop a"]));
|
|
}
|
|
|
|
#[test]
|
|
fn match_nested_enum_field_scrutinee_moves_inner_part() {
|
|
{
|
|
let n = match (mk(), D("y")).0.0 { Ok(d) => d, Err(_) => panic!() };
|
|
log(format!("got {}", n.0));
|
|
}
|
|
assert_eq!(take(), v(&["drop y", "got x", "drop x"]));
|
|
}
|
|
|
|
#[test]
|
|
fn match_scrutinee_field_bound_by_ref_drops_whole_temporary() {
|
|
{
|
|
match two().0 {
|
|
ref d => log(format!("in {}", d.0)),
|
|
}
|
|
log("after".to_string());
|
|
let n = match mk().0 { Ok(ref d) => d.0, Err(_) => "none" };
|
|
log(format!("got {}", n));
|
|
}
|
|
assert_eq!(take(), v(&["in a", "drop a", "drop b", "after", "drop x", "got x"]));
|
|
}
|
|
|
|
#[test]
|
|
fn match_scrutinee_field_arm_that_does_not_move() {
|
|
{
|
|
let n = match mk().0 { Err(_) => 0, _ => 1 };
|
|
log(format!("got {}", n));
|
|
}
|
|
assert_eq!(take(), v(&["drop x", "got 1"]));
|
|
}
|