diff --git a/compiler/forget/Cargo.lock b/compiler/forget/Cargo.lock index 0e15da8ffc..8129aefff1 100644 --- a/compiler/forget/Cargo.lock +++ b/compiler/forget/Cargo.lock @@ -638,6 +638,7 @@ dependencies = [ "estree", "indexmap 2.0.0", "serde", + "utils", ] [[package]] @@ -2806,6 +2807,7 @@ name = "utils" version = "0.1.0" dependencies = [ "bumpalo", + "stacker", ] [[package]] diff --git a/compiler/forget/crates/build-hir/src/build.rs b/compiler/forget/crates/build-hir/src/build.rs index af4d4fc82c..4cce6fc452 100644 --- a/compiler/forget/crates/build-hir/src/build.rs +++ b/compiler/forget/crates/build-hir/src/build.rs @@ -1,8 +1,10 @@ -use bumpalo::collections::{String, Vec}; +use bumpalo::{ + boxed::Box, + collections::{String, Vec}, +}; use estree::{ AssignmentTarget, BinaryExpression, BlockStatement, Expression, ForInit, ForStatement, - FunctionDeclaration, IfStatement, JsValue, Literal, Pattern, Statement, - VariableDeclarationKind, + FunctionExpression, IfStatement, JsValue, Literal, Pattern, Statement, VariableDeclarationKind, }; use hir::{ ArrayElement, BlockKind, BranchTerminal, Environment, ForTerminal, Function, GotoKind, @@ -24,11 +26,11 @@ use crate::{ /// that is not yet supported. pub fn build<'a>( env: &'a Environment<'a>, - fun: FunctionDeclaration, -) -> Result<&'a mut Function<'a>, BuildDiagnostic> { + fun: estree::Function, +) -> Result>, BuildDiagnostic> { let mut builder = Builder::new(env); - match fun.function.body { + match fun.body { Some(estree::FunctionBody::BlockStatement(body)) => { lower_block_statement(env, &mut builder, *body)? } @@ -44,8 +46,8 @@ pub fn build<'a>( } } - let mut params = Vec::with_capacity_in(fun.function.params.len(), &env.allocator); - for param in fun.function.params { + let mut params = Vec::with_capacity_in(fun.params.len(), &env.allocator); + for param in fun.params { match param { Pattern::Identifier(param) => { let identifier = lower_identifier_for_assignment( @@ -76,16 +78,18 @@ pub fn build<'a>( ); let body = builder.build()?; - Ok(env.alloc(Function { - id: fun - .function - .id - .map(|id| String::from_str_in(&id.name, &env.allocator)), - body, - params, - is_async: fun.function.is_async, - is_generator: fun.function.is_generator, - })) + Ok(Box::new_in( + Function { + id: fun + .id + .map(|id| String::from_str_in(&id.name, &env.allocator)), + body, + params, + is_async: fun.is_async, + is_generator: fun.is_generator, + }, + &env.allocator, + )) } fn lower_block_statement<'a>( @@ -421,6 +425,16 @@ fn lower_expression<'a>( }) } + Expression::FunctionExpression(expr) => { + let FunctionExpression { function, .. } = *expr; + let fun = build(env, function)?; + InstructionValue::Function(hir::FunctionExpression { + // TODO: collect dependencies! + dependencies: Vec::new_in(&env.allocator), + lowered_function: fun, + }) + } + _ => todo!("Lower expr {expr:#?}"), }; Ok(builder.push(value)) diff --git a/compiler/forget/crates/estree-codegen/src/ecmascript.json b/compiler/forget/crates/estree-codegen/src/ecmascript.json index 983b21a10f..bb21e4ec2a 100644 --- a/compiler/forget/crates/estree-codegen/src/ecmascript.json +++ b/compiler/forget/crates/estree-codegen/src/ecmascript.json @@ -43,6 +43,14 @@ "type": "bool", "optional": true, "rename": "async" + }, + "loc": { + "type": "Option", + "optional": true + }, + "range": { + "type": "Option", + "optional": true } } }, diff --git a/compiler/forget/crates/estree-swc/src/lib.rs b/compiler/forget/crates/estree-swc/src/lib.rs index d69050a41e..5bb560e2a8 100644 --- a/compiler/forget/crates/estree-swc/src/lib.rs +++ b/compiler/forget/crates/estree-swc/src/lib.rs @@ -6,8 +6,9 @@ use swc_core::common::errors::Handler; use swc_core::common::source_map::Pos; use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS}; use swc_core::ecma::ast::{ - AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, MemberExpr, MemberProp, - ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr, + AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Function, Ident, Lit, MemberExpr, + MemberProp, ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, + VarDeclOrExpr, }; use swc_core::ecma::parser::Syntax; use swc_core::ecma::transforms::base::resolver; @@ -122,32 +123,34 @@ fn convert_block_statement(cx: &Context, stmt: &BlockStmt) -> estree::BlockState } } +fn convert_function(cx: &Context, id: Option<&Ident>, fun: &Function) -> estree::Function { + estree::Function { + id: id.map(|id| estree::Identifier { + name: id.sym.to_string(), + binding: convert_binding(cx, id.span.ctxt), + loc: None, + range: convert_span(&id.span), + }), + params: fun + .params + .iter() + .map(|param| convert_pattern(cx, ¶m.pat)) + .collect(), + body: fun.body.as_ref().map(|body| { + estree::FunctionBody::BlockStatement(Box::new(convert_block_statement(cx, body))) + }), + is_async: fun.is_async, + is_generator: fun.is_generator, + loc: None, + range: convert_span(&fun.span), + } +} + fn convert_statement(cx: &Context, stmt: &Stmt) -> estree::Statement { match stmt { Stmt::Decl(Decl::Fn(item)) => { - let name = item.ident.sym.to_string(); estree::Statement::FunctionDeclaration(Box::new(estree::FunctionDeclaration { - function: estree::Function { - id: Some(estree::Identifier { - name, - binding: convert_binding(cx, item.ident.span.ctxt), - loc: None, - range: convert_span(&item.ident.span), - }), - params: item - .function - .params - .iter() - .map(|param| convert_pattern(cx, ¶m.pat)) - .collect(), - body: item.function.body.as_ref().map(|body| { - estree::FunctionBody::BlockStatement(Box::new(convert_block_statement( - cx, body, - ))) - }), - is_async: item.function.is_async, - is_generator: item.function.is_generator, - }, + function: convert_function(cx, Some(&item.ident), &item.function), loc: None, range: convert_span(&item.function.span), })) @@ -368,6 +371,13 @@ fn convert_expression(cx: &Context, expr: &Expr) -> estree::Expression { Expr::Member(expr) => { estree::Expression::MemberExpression(Box::new(convert_member_expression(cx, expr))) } + Expr::Fn(expr) => { + estree::Expression::FunctionExpression(Box::new(estree::FunctionExpression { + function: convert_function(cx, expr.ident.as_ref(), &expr.function), + loc: None, + range: convert_span(&expr.function.span), + })) + } _ => todo!("translate expression {:#?}", expr), } } diff --git a/compiler/forget/crates/estree/src/generated.rs b/compiler/forget/crates/estree/src/generated.rs index 7f88894ea4..474a658936 100644 --- a/compiler/forget/crates/estree/src/generated.rs +++ b/compiler/forget/crates/estree/src/generated.rs @@ -23,6 +23,10 @@ pub struct Function { #[serde(rename = "async")] #[serde(default)] pub is_async: bool, + #[serde(default)] + pub loc: Option, + #[serde(default)] + pub range: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct RegExpValue { diff --git a/compiler/forget/crates/fixtures/tests/fixtures/function-expressions.js b/compiler/forget/crates/fixtures/tests/fixtures/function-expressions.js new file mode 100644 index 0000000000..3c9a83cd13 --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/fixtures/function-expressions.js @@ -0,0 +1,5 @@ +function Component(props) { + const foo = function foo(x) { + return x + 1; + }; +} diff --git a/compiler/forget/crates/fixtures/tests/fixtures_test.rs b/compiler/forget/crates/fixtures/tests/fixtures_test.rs index 1471390e8c..83c1c0e303 100644 --- a/compiler/forget/crates/fixtures/tests/fixtures_test.rs +++ b/compiler/forget/crates/fixtures/tests/fixtures_test.rs @@ -33,7 +33,7 @@ fn fixtures() { if ix != 0 { output.push_str("\n\n"); } - match build(&environment, *fun) { + match build(&environment, fun.function) { Ok(mut fun) => { enter_ssa(&environment, &mut fun).unwrap(); eliminate_redundant_phis(&environment, &mut fun); @@ -56,6 +56,7 @@ fn fixtures() { } } + let output = output.trim(); assert_snapshot!(format!("Input:\n{input}\n\nOutput:\n{output}")); }); } diff --git a/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@function-expressions.js.snap b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@function-expressions.js.snap new file mode 100644 index 0000000000..4d5ef45e5a --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@function-expressions.js.snap @@ -0,0 +1,34 @@ +--- +source: crates/fixtures/tests/fixtures_test.rs +expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")" +input_file: crates/fixtures/tests/fixtures/function-expressions.js +--- +Input: +function Component(props) { + const foo = function foo(x) { + return x + 1; + }; +} + + +Output: +function Component( + unknown props$3, +) +entry bb0 +bb0 (block) + [0] #0 = Function @deps[] @context[]: + function foo( + unknown x$0, + ) + entry bb1 + bb1 (block) + [0] #0 = LoadLocal unknown x$0 + [1] #1 = 1 + [2] #2 = Binary unknown #0 + unknown #1 + [3] Return unknown #2 + [1] #1 = StoreLocal Const unknown foo$4 = unknown #0 + [2] #2 = + [3] Return unknown #2 + + diff --git a/compiler/forget/crates/hir/Cargo.toml b/compiler/forget/crates/hir/Cargo.toml index 834a8317a9..a044768f9c 100644 --- a/compiler/forget/crates/hir/Cargo.toml +++ b/compiler/forget/crates/hir/Cargo.toml @@ -10,3 +10,4 @@ bumpalo = { version = "3.13.0", features = ["boxed", "collections"] } estree = { path = "../estree" } indexmap = "2.0.0" serde = "1.0.164" +utils = { path = "../utils" } diff --git a/compiler/forget/crates/hir/src/instruction.rs b/compiler/forget/crates/hir/src/instruction.rs index 8bf7ec0f56..bd1f445425 100644 --- a/compiler/forget/crates/hir/src/instruction.rs +++ b/compiler/forget/crates/hir/src/instruction.rs @@ -1,9 +1,12 @@ use std::{cell::RefCell, fmt::Display, rc::Rc}; -use bumpalo::collections::{String, Vec}; +use bumpalo::{ + boxed::Box, + collections::{String, Vec}, +}; use estree::BinaryOperator; -use crate::{IdentifierId, InstrIx, InstructionId, ScopeId, Type}; +use crate::{Function, IdentifierId, InstrIx, InstructionId, ScopeId, Type}; #[derive(Debug)] pub struct Instruction<'a> { @@ -17,22 +20,23 @@ impl<'a> Instruction<'a> { F: FnMut(&mut LValue<'a>) -> (), { match &mut self.value { - InstructionValue::Array(_) => {} - InstructionValue::Binary(_) => {} InstructionValue::DeclareContext(instr) => { f(&mut instr.lvalue); } InstructionValue::DeclareLocal(instr) => { f(&mut instr.lvalue); } - InstructionValue::LoadContext(_) => {} - InstructionValue::LoadGlobal(_) => {} - InstructionValue::LoadLocal(_) => {} - InstructionValue::Primitive(_) => {} InstructionValue::StoreLocal(instr) => { f(&mut instr.lvalue); } - InstructionValue::Tombstone => {} + InstructionValue::Array(_) + | InstructionValue::Binary(_) + | InstructionValue::LoadContext(_) + | InstructionValue::LoadGlobal(_) + | InstructionValue::LoadLocal(_) + | InstructionValue::Primitive(_) + | InstructionValue::Function(_) + | InstructionValue::Tombstone => {} } } @@ -41,16 +45,17 @@ impl<'a> Instruction<'a> { F: FnMut(&mut IdentifierOperand<'a>) -> (), { match &mut self.value { - InstructionValue::Array(_) => {} - InstructionValue::Binary(_) => {} - InstructionValue::DeclareContext(_) => {} - InstructionValue::DeclareLocal(_) => {} - InstructionValue::LoadContext(_) => {} - InstructionValue::LoadGlobal(_) => {} InstructionValue::LoadLocal(instr) => f(&mut instr.place), - InstructionValue::Primitive(_) => {} - InstructionValue::StoreLocal(_) => {} - InstructionValue::Tombstone => {} + InstructionValue::Array(_) + | InstructionValue::Binary(_) + | InstructionValue::DeclareContext(_) + | InstructionValue::DeclareLocal(_) + | InstructionValue::LoadContext(_) + | InstructionValue::LoadGlobal(_) + | InstructionValue::Primitive(_) + | InstructionValue::StoreLocal(_) + | InstructionValue::Function(_) + | InstructionValue::Tombstone => {} } } } @@ -68,7 +73,7 @@ pub enum InstructionValue<'a> { DeclareContext(DeclareContext<'a>), DeclareLocal(DeclareLocal<'a>), // Destructure(Destructure<'a>), - // Function(Function<'a>), + Function(FunctionExpression<'a>), // JsxFragment(JsxFragment<'a>), // JsxText(JsxText<'a>), LoadContext(LoadContext), @@ -110,6 +115,12 @@ pub struct Binary { pub right: Operand, } +#[derive(Debug)] +pub struct FunctionExpression<'a> { + pub dependencies: Vec<'a, Operand>, + pub lowered_function: Box<'a, Function<'a>>, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Primitive<'a> { pub value: PrimitiveValue<'a>, diff --git a/compiler/forget/crates/hir/src/print.rs b/compiler/forget/crates/hir/src/print.rs index 6e30f9ab3f..16a3761cf3 100644 --- a/compiler/forget/crates/hir/src/print.rs +++ b/compiler/forget/crates/hir/src/print.rs @@ -1,5 +1,7 @@ use std::fmt::{Result, Write}; +use utils::ensure_sufficient_stack; + use crate::{ ArrayElement, BasicBlock, Function, Identifier, IdentifierOperand, Instruction, InstructionValue, LValue, Operand, Phi, PrimitiveValue, Terminal, TerminalValue, HIR, @@ -16,25 +18,28 @@ pub trait Print<'a> { impl<'a> Print<'a> for Function<'a> { fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result { - writeln!( - out, - "function {}(", - match &self.id { - Some(id) => id, - None => "", + ensure_sufficient_stack(|| { + writeln!( + out, + "function {}(", + match &self.id { + Some(id) => id, + None => "", + } + )?; + for param in &self.params { + write!(out, " ")?; + param.print(hir, out)?; + writeln!(out, ",")?; } - )?; - for param in &self.params { - write!(out, " ")?; - param.print(hir, out)?; - writeln!(out, ",")?; - } - writeln!(out, ")")?; - writeln!(out, "entry {}", self.body.entry)?; - for (_, block) in self.body.blocks.iter() { - block.print(hir, out)?; - } - Ok(()) + writeln!(out, ")")?; + writeln!(out, "entry {}", self.body.entry)?; + for (_, block) in self.body.blocks.iter() { + block.print(hir, out)?; + } + writeln!(out)?; + Ok(()) + }) } } @@ -146,6 +151,33 @@ impl<'a> Print<'a> for InstructionValue<'a> { write!(out, " {} ", value.operator)?; value.right.print(hir, out)?; } + InstructionValue::Function(value) => { + write!(out, "Function @deps[")?; + for (ix, dep) in value.dependencies.iter().enumerate() { + if ix != 0 { + write!(out, ", ")?; + } + dep.print(hir, out)?; + } + write!(out, "] @context[")?; + // for (ix, dep) in value.lowered_function.context.iter().enumerate() { + // if ix != 0 { + // write!(out, ", ")?; + // } + // dep.print(hir, out)?; + // } + writeln!(out, "]:")?; + let mut inner_output = String::new(); + value + .lowered_function + .print(&value.lowered_function.body, &mut inner_output)?; + let lines: Vec<_> = inner_output + .split("\n") + .map(|line| format!(" {}", line)) + .filter(|line| line.trim().len() != 0) + .collect(); + write!(out, "{}", lines.join("\n"))?; + } InstructionValue::Tombstone => { write!(out, "Tombstone!")?; } diff --git a/compiler/forget/crates/utils/Cargo.toml b/compiler/forget/crates/utils/Cargo.toml index 476d93c1a2..2d350ca5c4 100644 --- a/compiler/forget/crates/utils/Cargo.toml +++ b/compiler/forget/crates/utils/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bumpalo = { version = "3.13.0", features = ["boxed", "collections"] } \ No newline at end of file +bumpalo = { version = "3.13.0", features = ["boxed", "collections"] } +stacker = "0.1.15" diff --git a/compiler/forget/crates/utils/src/lib.rs b/compiler/forget/crates/utils/src/lib.rs index 174e1c3a3a..ce8ab25562 100644 --- a/compiler/forget/crates/utils/src/lib.rs +++ b/compiler/forget/crates/utils/src/lib.rs @@ -1,113 +1,5 @@ -use bumpalo::collections::Vec; +mod ensure_sufficient_stack; +mod retain_mut; -pub trait RetainMut { - fn retain_mut(&mut self, f: F) -> () - where - F: FnMut(&mut T) -> bool; -} - -impl<'a, T> RetainMut for Vec<'a, T> { - fn retain_mut(&mut self, mut f: F) -> () - where - F: FnMut(&mut T) -> bool, - { - // NOTE: implementation adapted from retain_mut crate - // which is in turn adapted from Rust stdlib - // https://docs.rs/retain_mut/latest/src/retain_mut/lib.rs.html#68-69 - - let original_len = self.len(); - // Avoid double drop if the drop guard is not executed, - // since we may make some holes during the process. - unsafe { self.set_len(0) }; - - // Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked] - // |<- processed len ->| ^- next to check - // |<- deleted cnt ->| - // |<- original_len ->| - // Kept: Elements which predicate returns true on. - // Hole: Moved or dropped element slot. - // Unchecked: Unchecked valid elements. - // - // This drop guard will be invoked when predicate or `drop` of element panicked. - // It shifts unchecked elements to cover holes and `set_len` to the correct length. - // In cases when predicate and `drop` never panick, it will be optimized out. - struct BackshiftOnDrop<'a, 'b, T> { - v: &'b mut Vec<'a, T>, - processed_len: usize, - deleted_cnt: usize, - original_len: usize, - } - - impl Drop for BackshiftOnDrop<'_, '_, T> { - fn drop(&mut self) { - if self.deleted_cnt > 0 { - // SAFETY: Trailing unchecked items must be valid since we never touch them. - unsafe { - std::ptr::copy( - self.v.as_ptr().add(self.processed_len), - self.v - .as_mut_ptr() - .add(self.processed_len - self.deleted_cnt), - self.original_len - self.processed_len, - ); - } - } - // SAFETY: After filling holes, all items are in contiguous memory. - unsafe { - self.v.set_len(self.original_len - self.deleted_cnt); - } - } - } - - let mut g = BackshiftOnDrop { - v: self, - processed_len: 0, - deleted_cnt: 0, - original_len, - }; - - fn process_loop( - original_len: usize, - f: &mut F, - g: &mut BackshiftOnDrop<'_, '_, T>, - ) where - F: FnMut(&mut T) -> bool, - { - while g.processed_len != original_len { - // SAFETY: Unchecked element must be valid. - let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) }; - if !f(cur) { - // Advance early to avoid double drop if `drop_in_place` panicked. - g.processed_len += 1; - g.deleted_cnt += 1; - // SAFETY: We never touch this element again after dropped. - unsafe { std::ptr::drop_in_place(cur) }; - // We already advanced the counter. - if DELETED { - continue; - } else { - break; - } - } - if DELETED { - // SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element. - // We use copy for move, and never touch this element again. - unsafe { - let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt); - std::ptr::copy_nonoverlapping(cur, hole_slot, 1); - } - } - g.processed_len += 1; - } - } - - // Stage 1: Nothing was deleted. - process_loop::(original_len, &mut f, &mut g); - - // Stage 2: Some elements were deleted. - process_loop::(original_len, &mut f, &mut g); - - // All item are processed. This can be optimized to `set_len` by LLVM. - drop(g); - } -} +pub use ensure_sufficient_stack::*; +pub use retain_mut::*; diff --git a/compiler/forget/crates/utils/src/retain_mut.rs b/compiler/forget/crates/utils/src/retain_mut.rs new file mode 100644 index 0000000000..174e1c3a3a --- /dev/null +++ b/compiler/forget/crates/utils/src/retain_mut.rs @@ -0,0 +1,113 @@ +use bumpalo::collections::Vec; + +pub trait RetainMut { + fn retain_mut(&mut self, f: F) -> () + where + F: FnMut(&mut T) -> bool; +} + +impl<'a, T> RetainMut for Vec<'a, T> { + fn retain_mut(&mut self, mut f: F) -> () + where + F: FnMut(&mut T) -> bool, + { + // NOTE: implementation adapted from retain_mut crate + // which is in turn adapted from Rust stdlib + // https://docs.rs/retain_mut/latest/src/retain_mut/lib.rs.html#68-69 + + let original_len = self.len(); + // Avoid double drop if the drop guard is not executed, + // since we may make some holes during the process. + unsafe { self.set_len(0) }; + + // Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked] + // |<- processed len ->| ^- next to check + // |<- deleted cnt ->| + // |<- original_len ->| + // Kept: Elements which predicate returns true on. + // Hole: Moved or dropped element slot. + // Unchecked: Unchecked valid elements. + // + // This drop guard will be invoked when predicate or `drop` of element panicked. + // It shifts unchecked elements to cover holes and `set_len` to the correct length. + // In cases when predicate and `drop` never panick, it will be optimized out. + struct BackshiftOnDrop<'a, 'b, T> { + v: &'b mut Vec<'a, T>, + processed_len: usize, + deleted_cnt: usize, + original_len: usize, + } + + impl Drop for BackshiftOnDrop<'_, '_, T> { + fn drop(&mut self) { + if self.deleted_cnt > 0 { + // SAFETY: Trailing unchecked items must be valid since we never touch them. + unsafe { + std::ptr::copy( + self.v.as_ptr().add(self.processed_len), + self.v + .as_mut_ptr() + .add(self.processed_len - self.deleted_cnt), + self.original_len - self.processed_len, + ); + } + } + // SAFETY: After filling holes, all items are in contiguous memory. + unsafe { + self.v.set_len(self.original_len - self.deleted_cnt); + } + } + } + + let mut g = BackshiftOnDrop { + v: self, + processed_len: 0, + deleted_cnt: 0, + original_len, + }; + + fn process_loop( + original_len: usize, + f: &mut F, + g: &mut BackshiftOnDrop<'_, '_, T>, + ) where + F: FnMut(&mut T) -> bool, + { + while g.processed_len != original_len { + // SAFETY: Unchecked element must be valid. + let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) }; + if !f(cur) { + // Advance early to avoid double drop if `drop_in_place` panicked. + g.processed_len += 1; + g.deleted_cnt += 1; + // SAFETY: We never touch this element again after dropped. + unsafe { std::ptr::drop_in_place(cur) }; + // We already advanced the counter. + if DELETED { + continue; + } else { + break; + } + } + if DELETED { + // SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element. + // We use copy for move, and never touch this element again. + unsafe { + let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt); + std::ptr::copy_nonoverlapping(cur, hole_slot, 1); + } + } + g.processed_len += 1; + } + } + + // Stage 1: Nothing was deleted. + process_loop::(original_len, &mut f, &mut g); + + // Stage 2: Some elements were deleted. + process_loop::(original_len, &mut f, &mut g); + + // All item are processed. This can be optimized to `set_len` by LLVM. + drop(g); + } +}