diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index 4455164783a..60020c08bd5 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -858,6 +858,31 @@ jobs: if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi 'exit' "$GUNBC_FLOOR_EXIT" + if: "!cancelled()" + - name: Run the slice cost controls + id: slice_cost_controls + run: |+ + GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' + 'set' '+e' + 'set' '-o' 'pipefail' + ('sh' '-e' '-c' 'cargo test --release -p v1-stage0-runtime --lib -- list_slice_tests') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" + GUNBC_FLOOR_EXIT="$?" + GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' + GUNBC_FLOOR_CLASS='structural' + GUNBC_FLOOR_SIGNATURE='' + if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi + if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi + 'exit' "$GUNBC_FLOOR_EXIT" + if: "!cancelled()" - name: "Stage0 mirrors match one emission by this seed: required CI build lane" id: stage0_regen diff --git a/dag/extdeps/languages/rust/emit.dag b/dag/extdeps/languages/rust/emit.dag index 2bffcb57d7d..69034eef1fa 100644 --- a/dag/extdeps/languages/rust/emit.dag +++ b/dag/extdeps/languages/rust/emit.dag @@ -76,8 +76,8 @@ data rust_simple_method_specs: List = [ { method_name: "first", template: "\{recv\}.first().cloned()", wraps_result: false }, { method_name: "enumerate", template: "\{recv\}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()", wraps_result: true }, { method_name: "chars", template: "\{recv\}.chars().map(|c| c as i64).collect::>()", wraps_result: true }, - { method_name: "skip", template: "\{recv\}.iter().cloned().skip(\{arg\} as usize).collect::>()", wraps_result: true }, - { method_name: "take", template: "\{recv\}.iter().cloned().take(\{arg\} as usize).collect::>()", wraps_result: true }, + { method_name: "skip", template: "v1_rt::list_skip(&\{recv\}, \{arg\})", wraps_result: true }, + { method_name: "take", template: "v1_rt::list_take(&\{recv\}, \{arg\})", wraps_result: true }, { method_name: "is_empty", template: "\{recv\}.is_empty()", wraps_result: false } ] diff --git a/dag/gunbc/repo/repo_self_build.dag b/dag/gunbc/repo/repo_self_build.dag index ec33246b355..5a5bbade815 100644 --- a/dag/gunbc/repo/repo_self_build.dag +++ b/dag/gunbc/repo/repo_self_build.dag @@ -107,6 +107,30 @@ fn repo_self_test_command() -> String { ) } +// A NAMED SUBSET OF ONE PACKAGE'S UNIT TESTS, for a control in a package the whole-package run above +// does not cover: the blocking rust-unit-tests lane runs repo_self_test_command, which is +// v1-compiler only. +// The filters are libtest's substring filters after `--`; an empty list would run the whole +// package, which is repo_self_test_command's request and not this one. +fn repo_self_targeted_test_command(package: String, filters: List) -> String { + join(concat(["cargo test", repo_self_build_profile_flag, "-p", package, "--lib", "--"], filters), " ") +} + +// The crate the emitted runtime v1_rt.rs is compiled in (the stage0 partition's foundation crate). +data repo_self_runtime_package: String = "v1-stage0-runtime" + +// THE EMITTED RUNTIME'S SLICE COST CONTROLS, ENROLLED. v1_rt list_slice_tests carries a RED that runs +// the copying skip / take templates the emit table used to render and a GREEN that runs +// list_skip / list_take, on non-ASCII code points, so the pair discriminates the quadratic rather +// than merely exercising the slice. Only this crate is named: the interpreter's slice and drop +// controls (v1_interpreter slice_cost_tests, value_depth_walker_tests) are in v1-compiler's own +// unit tests, which the blocking rust-unit-tests lane already runs whole (repo_self_test_command), +// and running them again here would be the same work twice. v1-stage0-runtime is outside that +// lane's package, so without this step its controls would only compile. +fn repo_self_slice_cost_control_command() -> String { + repo_self_targeted_test_command(package: repo_self_runtime_package, filters: ["list_slice_tests"]) +} + // THE LINT PASS OVER EVERY TARGET, as the one invocation DESIGN's "Building & checks" names. // `--all-targets` is the whole point: the test and example targets outside `--lib` and `--bins` // are compiled by no build command above and executed by no test command, so before this row diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index 92c28cdff30..aa368984eb4 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -166,7 +166,7 @@ import gunbc.fleet_release_bins_key { release_bins_pack_object_script, release_bins_pack_object_path_expression, compiler_pair_candidate_artifact_name, compiler_pair_candidate_retention_whole_days, } -import gunbc.repo_self_build { repo_self_build_command, repo_self_clippy_command, repo_self_test_command } +import gunbc.repo_self_build { repo_self_build_command, repo_self_clippy_command, repo_self_test_command, repo_self_slice_cost_control_command } import extdeps.exec.command { shell_command_render } import extdeps.github.expressions { interpolate, ContextAccess, Needs, Github, Expression, default_expression_spellings } import extdeps.github.log_annotations { emit_line_shell } @@ -270,6 +270,28 @@ fn compiler_gate_hosted_build_prelude_bound_steps() -> List Step { + RunStep { + name: Present { value: compiler_gate_slice_cost_step_name }, + id: Present { value: compiler_gate_slice_cost_step_id }, + run: concat(wrap_command_with_floor_attempt_receipt(command: repo_self_slice_cost_control_command()), "\n"), + shell: none, + env: none, + working_directory: none, + if_condition: Present { value: witness_step_status_guard_expression() }, + continue_on_error: none, + timeout_minutes: none + } +} + fn compiler_gate_lint_step() -> Step { RunStep { name: Present { value: compiler_gate_clippy_step_name }, @@ -856,6 +878,11 @@ fn compiler_gate_generated_bound_steps() -> List { role: consumes_only(capabilities: [CargoCapability]), step_name: compiler_gate_clippy_step_name, }, + CompilerGateBoundStep { + step: compiler_gate_slice_cost_step(), + role: consumes_only(capabilities: [CargoCapability]), + step_name: compiler_gate_slice_cost_step_name, + }, CompilerGateBoundStep { step: compiler_gate_stage0_regen_step(), role: consumes_only(capabilities: [RustfmtCapability]), @@ -923,6 +950,7 @@ fn compiler_gate_floor_scripts_are_renderable() -> Bool { wrap_command_is_renderable(command: witness_floor_run_script()) && wrap_command_is_renderable(command: build_lane_run_script()) && wrap_command_is_renderable(command: repo_self_clippy_command()) + && wrap_command_is_renderable(command: repo_self_slice_cost_control_command()) && wrap_command_is_renderable(command: repo_self_build_command(bins: witness_floor_required_bins)) && required_ci_measurement_scripts_are_renderable() } diff --git a/dag/std/primitives.dag b/dag/std/primitives.dag index 70d251b14aa..e0c5cb86ca9 100644 --- a/dag/std/primitives.dag +++ b/dag/std/primitives.dag @@ -409,12 +409,31 @@ data append_contract: PrimitiveContract = { carrier_cost: CarrierSensitive { ephemeral_work: "n + m", persistent_work: "log(n + m)" } } +// SKIP AND TAKE ARE THE SLICE, AND ON THE PERSISTENT CARRIER THEY SHARE THE RECEIVER'S TREE. A walk +// that reads a code-point or octet list by offset slices it as `xs |> skip(n: a) |> take(n: b - a)`; +// when either half copied its result, each slice paid the remainder and the walk was quadratic in +// the list (DESIGN section 6, bare minimum cost). Both realizations hold the list as an RRB vector +// (the interpreter's Value::List, v1_rt's im::Vector), whose split is logarithmic, so the slice +// costs log len to form and only its own length to read. That removes the quadratic remainder +// copying; it does not make an offset walk linear, since every member read is get_contract's +// log n on this carrier, so a walk that reads each offset is O(n log n). +// +// THE COUNT IS CLAMPED, AND A NEGATIVE COUNT IS THE WHOLE LIST. Both realizations take +// k = if n < 0 then len else min(n, len): skip keeps len - k members and take keeps k. data skip_contract: PrimitiveContract = { name: "skip", - work: "n", - output_size: "max(0, len - n)", + work: "log len", + output_size: "len - k where k = if n < 0 then len else min(n, len)", certainty: Proven, - carrier_cost: CarrierInsensitive + carrier_cost: CarrierSensitive { ephemeral_work: "len - k where k = if n < 0 then len else min(n, len)", persistent_work: "log len" } +} + +data take_contract: PrimitiveContract = { + name: "take", + work: "log len", + output_size: "k where k = if n < 0 then len else min(n, len)", + certainty: Proven, + carrier_cost: CarrierSensitive { ephemeral_work: "k where k = if n < 0 then len else min(n, len)", persistent_work: "log len" } } data zip_contract: PrimitiveContract = { @@ -791,6 +810,7 @@ fn primitive_contract_roster() -> List { list_push_contract, append_contract, skip_contract, + take_contract, zip_contract, enumerate_contract, empty_map_contract, diff --git a/dag/test/claim/code_point_slice_witness_test.dag b/dag/test/claim/code_point_slice_witness_test.dag new file mode 100644 index 00000000000..eebf6e63af8 --- /dev/null +++ b/dag/test/claim/code_point_slice_witness_test.dag @@ -0,0 +1,94 @@ +module test.claim.code_point_slice_witness + +import std.algebra { Cons } +import std.coercion { unicode_scalar_unfold, unicode_scalar_fold } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// A BODY READ BY OFFSET, THROUGH THE CODE-POINT SLICE. The text is unfolded to its code points +// once; every later read is one member (`get`), the length (`count`) or a bounded slice +// (`skip` then `take`) folded back to host text. std.primitives get_contract, count_contract, +// skip_contract and take_contract price each of those at log len or better on the persistent +// carrier, so the walk is O(n log n) in the body on every input -- the remainder copying that made +// it quadratic is gone. Host-text char_at / substring are O(offset) per read on non-ASCII text in +// the interpreter and on all text in emitted Rust, which is the quadratic this surface avoids. +// The bodies here are non-ASCII on purpose: that is the input the host-text walk is quadratic on. +// +// The wire shape is the length-prefixed field `:`, repeated. +type CodePointField { + value: String + next: Int +} + +fn cps_find(cps: List, at: Int, stop: Int) -> Int? { + match get(xs: cps, index: at) { + Absent => none + Present { value: c } => if c == stop { Present { value: at } } else { cps_find(cps: cps, at: at + 1, stop: stop) } + } +} + +fn cps_slice_text(cps: List, start: Int, end: Int) -> String { + unicode_scalar_fold(xs: cps |> skip(n: start) |> take(n: end - start)) +} + +fn cps_field(cps: List, at: Int) -> CodePointField? { + match cps_find(cps: cps, at: at, stop: code_point(c: ":")) { + Absent => none + Present { value: colon } => + match parse_int(s: cps_slice_text(cps: cps, start: at, end: colon)) { + Absent => none + Present { value: n } => + if n < 0 || colon + 1 + n > count(cps) { + none + } else { + Present { value: CodePointField { value: cps_slice_text(cps: cps, start: colon + 1, end: colon + 1 + n), next: colon + 1 + n } } + } + } + } +} + +fn cps_fields(cps: List, at: Int, acc: List) -> List? { + if at == count(cps) { + Present { value: reverse(acc) } + } else { + match cps_field(cps: cps, at: at) { + Absent => none + Present { value: f } => cps_fields(cps: cps, at: f.next, acc: Cons { head: f.value, tail: acc }) + } + } +} + +fn decode_fields(body: String) -> List? { + cps_fields(cps: unicode_scalar_unfold(s: body), at: 0, acc: []) +} + +// The body doubled `times` times: linear to build, so the measured walk is the only quadratic +// candidate in the claim. +fn doubled(s: String, times: Int) -> String { + if times <= 0 { s } else { doubled(s: concat(s, s), times: times - 1) } +} + +// POSITIVE CONTROL: the slice reads the right code points, including astral ones (one code point, +// four UTF-8 bytes) and a field whose length prefix is two digits. +test fn code_point_slice_decodes_non_ascii_fields() -> Bool { + decode_fields(body: "2:\u{e9}\u{4e2d}3:\u{1f600}ab0:12:\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}") == Present { value: ["\u{e9}\u{4e2d}", "\u{1f600}ab", "", "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"] } +} + +// A length prefix that runs past the body is refused, not truncated. +test fn code_point_slice_refuses_an_overlong_field() -> Bool { + decode_fields(body: "4:\u{e9}\u{4e2d}") == none +} + +// THE WALK OVER MANY FIELDS: 512 non-ASCII fields, 3,072 code points, sized under the floor's +// new-witness eval-step budget (v2.workflow.required_floor). This claim's subject is that the +// decode stays correct field after field; the COST of the walk is not measured here, because eval +// steps cannot see a host-level copy. The cost controls that can, RED on the copying forms and +// GREEN on the sharing ones, are gunbc.repo_self_build repo_self_slice_cost_control_command, run on +// the merge path by the generated job's slice cost step. +test fn code_point_slice_walk_is_correct_at_scale() -> Bool { + match decode_fields(body: doubled(s: "3:\u{e9}\u{4e2d}\u{1f600}", times: 9)) { + Absent => false + Present { value: fs } => count(fs) == 512 && fs |> all(f => f == "\u{e9}\u{4e2d}\u{1f600}") + } +} diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index b5477ac289f..b51c255775c 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -13565,7 +13565,7 @@ fn emit_rust_first_method_call(receiver: Node, registry: Map, let skip_args = method_arg_nodes(texpr: receiver) let recv_str = emit_typed_expr(texpr: skip_recv, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) let n_str = emit_typed_first_arg(args: skip_args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) - concat(recv_str, ".iter().cloned().skip(", n_str, " as usize).next()") + concat(recv_str, ".get((", n_str, ") as usize).cloned()") } else { let recv_str = emit_typed_expr(texpr: receiver, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) concat(recv_str, ".first().cloned()") diff --git a/src/v1/runtime_rust.dag b/src/v1/runtime_rust.dag index 5129153804f..df7ffedca8b 100644 --- a/src/v1/runtime_rust.dag +++ b/src/v1/runtime_rust.dag @@ -255,6 +255,118 @@ fn rt_collection_ops() -> String { "pub fn map_values(m: &HashMap) -> Vec { m.values().cloned().collect() }\n\n", "pub fn list_concat(mut a: Vec, b: Vec) -> Vec { a.append(b); a }\n\n", "pub fn list_push(mut list: Vec, item: T) -> Vec { list.push_back(item); list }\n\n", + "/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members,\n", + "/// sharing the receiver's tree in O(log n) instead of copying the remainder, so a walk that\n", + "/// slices by offset is O(n log n) rather than quadratic. A negative `n` skips every member, as\n", + "/// the copying form's saturating `n as usize` did.\n", + "pub fn list_skip(items: &Vec, n: i64) -> Vec \{\n", + " let k = if n < 0 \{\n", + " items.len()\n", + " \} else \{\n", + " (n as usize).min(items.len())\n", + " \};\n", + " items.skip(k)\n", + "\}\n", + "\n", + "/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing\n", + "/// the receiver's tree. A negative `n` keeps every member, as the copying form did.\n", + "pub fn list_take(items: &Vec, n: i64) -> Vec \{\n", + " let k = if n < 0 \{\n", + " items.len()\n", + " \} else \{\n", + " (n as usize).min(items.len())\n", + " \};\n", + " items.take(k)\n", + "\}\n", + "\n", + "#[cfg(test)]\n", + "mod list_slice_tests \{\n", + " use super::*;\n", + "\n", + " thread_local! \{\n", + " static CLONES: Cell = const \{ Cell::new(0) \};\n", + " \}\n", + "\n", + " /// A code point whose every copy is counted, so a slice's cost is read off the carrier's\n", + " /// own work rather than a clock.\n", + " #[derive(Debug, PartialEq)]\n", + " struct CountedCodePoint(u32);\n", + "\n", + " impl Clone for CountedCodePoint \{\n", + " fn clone(&self) -> Self \{\n", + " CLONES.with(|c| c.set(c.get() + 1));\n", + " CountedCodePoint(self.0)\n", + " \}\n", + " \}\n", + "\n", + " fn clones_during(f: impl FnOnce() -> R) -> (R, u64) \{\n", + " let before = CLONES.with(|c| c.get());\n", + " let r = f();\n", + " (r, CLONES.with(|c| c.get()) - before)\n", + " \}\n", + "\n", + " // Non-ASCII on purpose: emitted char_at / substring over a bare &str are O(offset) here,\n", + " // which is the walk the code-point slice replaces.\n", + " fn non_ascii_code_points(n: usize) -> Vec \{\n", + " [0xe9u32, 0x4e2d, 0x1f600, 0x61]\n", + " .iter()\n", + " .cycle()\n", + " .take(n)\n", + " .map(|cp| CountedCodePoint(*cp))\n", + " .collect()\n", + " \}\n", + "\n", + " const N: usize = 200_000;\n", + " // im's RRB split copies at most one boundary chunk per tree level; this is that bound with\n", + " // headroom, and three orders of magnitude below N.\n", + " const SLICE_COPY_BUDGET: u64 = 2_048;\n", + "\n", + " #[test]\n", + " fn a_slice_from_the_middle_copies_only_boundary_chunks() \{\n", + " let cps = Rc::new(non_ascii_code_points(N));\n", + " let (slice, copied) = clones_during(|| list_take(&list_skip(&cps, (N / 2) as i64), 7));\n", + " assert!(\n", + " copied <= SLICE_COPY_BUDGET,\n", + " \"skip+take copied \{\} members of \{\}\",\n", + " copied,\n", + " N\n", + " );\n", + " let expected: std::vec::Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect();\n", + " assert_eq!(\n", + " slice.iter().map(|c| c.0).collect::>(),\n", + " expected\n", + " );\n", + " \}\n", + "\n", + " // THE RED CONTROL: the extdeps.languages.rust.emit skip / take templates this runtime pair\n", + " // replaced, spelled as they emitted, on the same input and bound. It must exceed the budget,\n", + " // or the budget does not discriminate the quadratic.\n", + " #[test]\n", + " fn the_copying_template_form_exceeds_the_budget() \{\n", + " let cps = Rc::new(non_ascii_code_points(N));\n", + " let (_, copied) = clones_during(|| \{\n", + " let rest = cps.iter().cloned().skip(N / 2).collect::>();\n", + " rest.iter().cloned().take(7).collect::>()\n", + " \});\n", + " assert!(\n", + " copied > SLICE_COPY_BUDGET,\n", + " \"copying form copied only \{\}\",\n", + " copied\n", + " );\n", + " \}\n", + "\n", + " #[test]\n", + " fn negative_and_overlong_counts_keep_the_copying_forms_reading() \{\n", + " let xs: Vec = (0..10).collect();\n", + " for n in [-3i64, 0, 4, 10, 11, i64::MAX] \{\n", + " let copying_skip = xs.iter().cloned().skip(n as usize).collect::>();\n", + " let copying_take = xs.iter().cloned().take(n as usize).collect::>();\n", + " assert_eq!(list_skip(&xs, n), copying_skip, \"skip \{\}\", n);\n", + " assert_eq!(list_take(&xs, n), copying_take, \"take \{\}\", n);\n", + " \}\n", + " \}\n", + "\}\n", + "\n", "pub fn append(list: Rc>, item: T) -> Vec {\n", " let mut v = (*list).clone();\n", " v.push_back(item);\n", diff --git a/src/v1/stage0/src/extdeps_container_oci_digest.rs b/src/v1/stage0/src/extdeps_container_oci_digest.rs index 60344d27275..71788f3a266 100644 --- a/src/v1/stage0/src/extdeps_container_oci_digest.rs +++ b/src/v1/stage0/src/extdeps_container_oci_digest.rs @@ -259,7 +259,7 @@ pub fn oci_wire_digest_parts(raw: String) -> Option> { std::option::Option::None } else { match parts.clone().first().cloned() { - Some(algorithm) => match parts.clone().iter().cloned().skip(1 as usize).next() { + Some(algorithm) => match parts.clone().get((1) as usize).cloned() { Some(encoded) => Some(oci_wire_digest_parts_from_split( algorithm.clone(), encoded.clone(), diff --git a/src/v1/stage0/src/extdeps_languages_rust_emit.rs b/src/v1/stage0/src/extdeps_languages_rust_emit.rs index 0ec6134ed39..edcdd4fdc23 100644 --- a/src/v1/stage0/src/extdeps_languages_rust_emit.rs +++ b/src/v1/stage0/src/extdeps_languages_rust_emit.rs @@ -90,7 +90,7 @@ pub fn rust_container_templates() -> Rc> { pub fn rust_simple_method_specs() -> Rc>> { thread_local! { static CACHED: Rc>> = { - serde_json::from_str("[{\"method_name\": \"count\", \"template\": \"({recv}.len() as i64)\", \"wraps_result\": false}, {\"method_name\": \"join\", \"template\": \"{recv}.join(&{arg})\", \"wraps_result\": false}, {\"method_name\": \"split\", \"template\": \"{recv}.split(&{arg}).map(|s| s.to_string()).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"last\", \"template\": \"{recv}.last().cloned()\", \"wraps_result\": false}, {\"method_name\": \"first\", \"template\": \"{recv}.first().cloned()\", \"wraps_result\": false}, {\"method_name\": \"enumerate\", \"template\": \"{recv}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"chars\", \"template\": \"{recv}.chars().map(|c| c as i64).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"skip\", \"template\": \"{recv}.iter().cloned().skip({arg} as usize).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"take\", \"template\": \"{recv}.iter().cloned().take({arg} as usize).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"is_empty\", \"template\": \"{recv}.is_empty()\", \"wraps_result\": false}]") + serde_json::from_str("[{\"method_name\": \"count\", \"template\": \"({recv}.len() as i64)\", \"wraps_result\": false}, {\"method_name\": \"join\", \"template\": \"{recv}.join(&{arg})\", \"wraps_result\": false}, {\"method_name\": \"split\", \"template\": \"{recv}.split(&{arg}).map(|s| s.to_string()).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"last\", \"template\": \"{recv}.last().cloned()\", \"wraps_result\": false}, {\"method_name\": \"first\", \"template\": \"{recv}.first().cloned()\", \"wraps_result\": false}, {\"method_name\": \"enumerate\", \"template\": \"{recv}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"chars\", \"template\": \"{recv}.chars().map(|c| c as i64).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"skip\", \"template\": \"v1_rt::list_skip(&{recv}, {arg})\", \"wraps_result\": true}, {\"method_name\": \"take\", \"template\": \"v1_rt::list_take(&{recv}, {arg})\", \"wraps_result\": true}, {\"method_name\": \"is_empty\", \"template\": \"{recv}.is_empty()\", \"wraps_result\": false}]") .expect("valid data definition") }; } diff --git a/src/v1/stage0/src/extdeps_uri.rs b/src/v1/stage0/src/extdeps_uri.rs index 303138dbac4..919c2a4bb70 100644 --- a/src/v1/stage0/src/extdeps_uri.rs +++ b/src/v1/stage0/src/extdeps_uri.rs @@ -118,18 +118,15 @@ pub fn uri_from_wire(url: String) -> Option> { ParsedHrefScheme::UnknownHref => std::option::Option::None, ParsedHrefScheme::HrefScheme { scheme: scheme, .. } => { let prefix = uri_scheme_wire(scheme.clone()); - let locator = Rc::new( - Rc::new( + let locator = Rc::new(v1_rt::list_skip( + &Rc::new( s.clone() .split(&prefix.clone()) .map(|s| s.to_string()) .collect::>(), - ) - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + ), + 1, + )) .join(&prefix.clone()); if (v1_rt::starts_with(s.clone(), prefix.clone()) && !(locator.clone() == "".to_string())) diff --git a/src/v1/stage0/src/extdeps_uri_path.rs b/src/v1/stage0/src/extdeps_uri_path.rs index 1c6f7ff7508..83385bc9570 100644 --- a/src/v1/stage0/src/extdeps_uri_path.rs +++ b/src/v1/stage0/src/extdeps_uri_path.rs @@ -176,13 +176,7 @@ pub fn parse_segment_tokens(seg: String) -> Rc { }) } }; - let after_open = match before_and_rest - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let after_open = match before_and_rest.clone().get((1) as usize).cloned() { Some(r) => r.clone(), std::option::Option::None => { return Rc::new(PathSegmentTokensResult::MalformedPathSegment { @@ -216,13 +210,7 @@ pub fn parse_segment_tokens(seg: String) -> Rc { }) } }; - let suffix = match name_and_suffix - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let suffix = match name_and_suffix.clone().get((1) as usize).cloned() { Some(s) => s.clone(), std::option::Option::None => { return Rc::new(PathSegmentTokensResult::MalformedPathSegment { @@ -304,14 +292,7 @@ pub fn match_path_segments(path_only: String) -> Rc> { match raw_segs.clone().first().cloned() { Some(lead) => { if (lead.clone() == "".to_string()) { - Rc::new( - raw_segs - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&raw_segs.clone(), 1)) } else { raw_segs.clone() } @@ -337,21 +318,8 @@ pub fn match_path_tokens( Some(tok) => match segs.clone().first().cloned() { std::option::Option::None => Rc::new(PathTemplateMatch::PathNotMatched), Some(seg) => match (*match_path_tokens( - Rc::new( - tokens - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - segs.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&tokens.clone(), 1)), + Rc::new(v1_rt::list_skip(&segs.clone(), 1)), )) .clone() { @@ -461,51 +429,48 @@ pub fn parse_path_template(raw: String) -> Rc { tokens: first_tokens, .. } => { - let parsed = Rc::new( - segments - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - Rc::new(PathTemplateParseResult::ParsedPathTemplate { - template: Rc::new(PathTemplate { - tokens: first_tokens.clone(), - }), - }), - |acc: Rc, seg: String| match (*acc.clone()).clone() - { - PathTemplateParseResult::MalformedPathTemplate { .. } => acc.clone(), - PathTemplateParseResult::ParsedPathTemplate { - template: path, .. - } => match (*parse_segment_tokens(seg.clone())).clone() { - PathSegmentTokensResult::MalformedPathSegment { - segment: s, - reason: r, - .. - } => Rc::new(PathTemplateParseResult::MalformedPathTemplate { - raw: raw.clone(), - segment: s.clone(), - reason: r.clone(), + let parsed = Rc::new(v1_rt::list_skip(&segments.clone(), 1)) + .iter() + .cloned() + .fold( + Rc::new(PathTemplateParseResult::ParsedPathTemplate { + template: Rc::new(PathTemplate { + tokens: first_tokens.clone(), }), - PathSegmentTokensResult::ParsedSegmentTokens { - tokens: seg_tokens, + }), + |acc: Rc, seg: String| match (*acc.clone()) + .clone() + { + PathTemplateParseResult::MalformedPathTemplate { .. } => { + acc.clone() + } + PathTemplateParseResult::ParsedPathTemplate { + template: path, .. - } => Rc::new(PathTemplateParseResult::ParsedPathTemplate { - template: Rc::new(PathTemplate { - tokens: v1_rt::concat( - path.tokens.clone(), - seg_tokens.clone(), - ), + } => match (*parse_segment_tokens(seg.clone())).clone() { + PathSegmentTokensResult::MalformedPathSegment { + segment: s, + reason: r, + .. + } => Rc::new(PathTemplateParseResult::MalformedPathTemplate { + raw: raw.clone(), + segment: s.clone(), + reason: r.clone(), }), - }), + PathSegmentTokensResult::ParsedSegmentTokens { + tokens: seg_tokens, + .. + } => Rc::new(PathTemplateParseResult::ParsedPathTemplate { + template: Rc::new(PathTemplate { + tokens: v1_rt::concat( + path.tokens.clone(), + seg_tokens.clone(), + ), + }), + }), + }, }, - }, - ); + ); parsed } }, @@ -520,10 +485,8 @@ pub fn uri_query_string(path: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(q) => q.clone(), std::option::Option::None => "".to_string(), @@ -560,10 +523,8 @@ pub fn uri_query_param(path: String, key: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(x) => x.clone(), std::option::Option::None => "".to_string(), diff --git a/src/v1/stage0/src/extdeps_version_semver.rs b/src/v1/stage0/src/extdeps_version_semver.rs index da6901c2ac6..43fd8151ef5 100644 --- a/src/v1/stage0/src/extdeps_version_semver.rs +++ b/src/v1/stage0/src/extdeps_version_semver.rs @@ -107,12 +107,8 @@ pub fn semver_compare_identifiers( Some(ah) => match b.clone().first().cloned() { Some(bh) => match semver_compare_identifier(ah.clone(), bh.clone()) { Ordering::Equal => { - let __tco_0 = Rc::new( - a.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - b.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&a, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&b, 1)); __tco_loop_a = __tco_0; __tco_loop_b = __tco_1; continue; @@ -217,13 +213,7 @@ pub fn semver_core_of_dotted(text: String) -> Option> { } pub fn semver_dotted_part(parts: Rc>, index: i64) -> Option { - match parts - .clone() - .iter() - .cloned() - .skip(index.clone() as usize) - .next() - { + match parts.clone().get((index.clone()) as usize).cloned() { std::option::Option::None => std::option::Option::None, Some(w) => match v1_rt::parse_int(w.clone()) { std::option::Option::None => std::option::Option::None, diff --git a/src/v1/stage0/src/std_graph.rs b/src/v1/stage0/src/std_graph.rs index a813e4db5eb..12b3c64d336 100644 --- a/src/v1/stage0/src/std_graph.rs +++ b/src/v1/stage0/src/std_graph.rs @@ -371,13 +371,7 @@ pub fn is_lexicographic_descent(mut __tco_loop_evidence: Rc break true; } DescentEvidence::NonIncreasing => { - let __tco_0 = Rc::new( - evidence - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&evidence, 1)); __tco_loop_evidence = __tco_0; continue; } diff --git a/src/v1/stage0/src/std_integer.rs b/src/v1/stage0/src/std_integer.rs index bbdca20220c..dea273c3614 100644 --- a/src/v1/stage0/src/std_integer.rs +++ b/src/v1/stage0/src/std_integer.rs @@ -129,13 +129,7 @@ pub fn first_uint8_out_of_range(mut __tco_loop_remaining: Rc>) -> Optio break Some(o.clone()); } UInt8Result::UInt8Ready { value: _, .. } => { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); __tco_loop_remaining = __tco_0; continue; } diff --git a/src/v1/stage0/src/std_occurrence_identity.rs b/src/v1/stage0/src/std_occurrence_identity.rs index 299c52ca17a..454b38f5d1b 100644 --- a/src/v1/stage0/src/std_occurrence_identity.rs +++ b/src/v1/stage0/src/std_occurrence_identity.rs @@ -549,14 +549,10 @@ pub fn occurrence_id_list_is_prefix_of( Some(head) => { if occurrence_id_eq(head.clone(), expected.clone()) { Rc::new(OccurrenceIdListPrefixAcc { - path_remaining: Rc::new( - acc.path_remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + path_remaining: Rc::new(v1_rt::list_skip( + &acc.path_remaining.clone(), + 1, + )), ok: true, }) } else { diff --git a/src/v1/stage0/src/std_realization_schedule.rs b/src/v1/stage0/src/std_realization_schedule.rs index 452e02181d4..8f1847f187c 100644 --- a/src/v1/stage0/src/std_realization_schedule.rs +++ b/src/v1/stage0/src/std_realization_schedule.rs @@ -831,10 +831,8 @@ pub fn string_list_eq( break false; } else { { - let __tco_0 = - Rc::new(left.iter().cloned().skip(1 as usize).collect::>()); - let __tco_1 = - Rc::new(right.iter().cloned().skip(1 as usize).collect::>()); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; @@ -867,12 +865,8 @@ pub fn schedule_witness_entry_list_eq( break false; } else { { - let __tco_0 = Rc::new( - left.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - right.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; @@ -980,12 +974,8 @@ pub fn runnable_batch_eq( break false; } else { { - let __tco_0 = Rc::new( - left.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - right.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; @@ -1024,12 +1014,8 @@ pub fn schedule_eq(mut __tco_loop_left: Schedule, mut __tco_loop_right: Schedule break false; } else { { - let __tco_0 = Rc::new( - left.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - right.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; diff --git a/src/v1/stage0/src/std_repair_input_origin.rs b/src/v1/stage0/src/std_repair_input_origin.rs index 283034fcc6e..e49ccbd854c 100644 --- a/src/v1/stage0/src/std_repair_input_origin.rs +++ b/src/v1/stage0/src/std_repair_input_origin.rs @@ -95,14 +95,10 @@ pub fn candidate_spelling_from_text(name: String) -> Rc { ); if ((segments.clone().len() as i64) > 1) { Rc::new(CandidateSpelling::QualifiedCandidate { - authored_qualifier: Rc::new( - segments - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub((segments.clone().len() as i64), 1) as usize) - .collect::>(), - ) + authored_qualifier: Rc::new(v1_rt::list_take( + &segments.clone(), + v1_rt::int_sub((segments.clone().len() as i64), 1), + )) .join(&".".to_string()), member: match segments.clone().last().cloned() { Some(member) => member.clone(), diff --git a/src/v1/stage0/src/std_source_annotation.rs b/src/v1/stage0/src/std_source_annotation.rs index 64e9feefea5..687ac4e8be5 100644 --- a/src/v1/stage0/src/std_source_annotation.rs +++ b/src/v1/stage0/src/std_source_annotation.rs @@ -141,15 +141,7 @@ pub fn admit_annotations(result: Rc) -> Rc Rc::new(AnnotationAdmission::AnnotationsRefused { refusals: Rc::new(FreeSemigroup { head: head.clone(), - tail: Rc::new( - result - .refusals - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + tail: Rc::new(v1_rt::list_skip(&result.refusals.clone(), 1)), _phantom: std::marker::PhantomData, }), }), @@ -407,14 +399,7 @@ pub fn keyed_annotation_rows_agree( agreed: false, }), Some(other) => Rc::new(KeyedAnnotationRowWalk { - rest: Rc::new( - acc.rest - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + rest: Rc::new(v1_rt::list_skip(&acc.rest.clone(), 1)), agreed: ((acc.agreed.clone() && (row.subject_key.clone() == other.subject_key.clone())) && (row.text.clone() == other.text.clone())), diff --git a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs index c5c5b0b9a60..44905a9020b 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -271,7 +271,7 @@ pub fn first_or_empty(items: Rc>) -> String { } pub fn second_or_empty(items: Rc>) -> String { - match items.clone().iter().cloned().skip(1 as usize).next() { + match items.clone().get((1) as usize).cloned() { Some(v) => v.clone(), std::option::Option::None => "".to_string(), } diff --git a/src/v1/stage0/src/v1_compiler_complexity.rs b/src/v1/stage0/src/v1_compiler_complexity.rs index fcbdbcfd653..7d32976ba45 100644 --- a/src/v1/stage0/src/v1_compiler_complexity.rs +++ b/src/v1/stage0/src/v1_compiler_complexity.rs @@ -358,12 +358,7 @@ pub fn iteration_element_name( { let params = crate::v1_std_core::lambda_param_names_at(lambda.clone(), si.clone()); match method_callback_element_position(method_semantics.clone()) { - Some(pos) => params - .clone() - .iter() - .cloned() - .skip(pos.clone() as usize) - .next(), + Some(pos) => params.clone().get((pos.clone()) as usize).cloned(), std::option::Option::None => std::option::Option::None, } } @@ -710,14 +705,10 @@ pub fn parser_block_state_progress( match stmts.clone().last().cloned() { std::option::Option::None => DescentEvidence::DescentUnknown, Some(last_stmt) => { - let leading = Rc::new( - stmts - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub((stmts.clone().len() as i64), 1) as usize) - .collect::>(), - ); + let leading = Rc::new(v1_rt::list_take( + &stmts.clone(), + v1_rt::int_sub((stmts.clone().len() as i64), 1), + )); let final_env = leading.iter().cloned().fold( env.clone(), |acc_env: Rc, stmt: Rc| match (*stmt.expr_data.clone()) @@ -1364,13 +1355,10 @@ pub fn parser_success_progress( } } ExprData::ExprBlock => { - let prefix = v1_rt::reverse(Rc::new( - v1_rt::reverse(expr.children.clone()) - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - )); + let prefix = v1_rt::reverse(Rc::new(v1_rt::list_skip( + &v1_rt::reverse(expr.children.clone()), + 1, + ))); let acc = prefix.iter().cloned().fold( Rc::new(ParserProgressAcc { edges: Rc::new(vec![]), @@ -1486,14 +1474,7 @@ pub fn infer_parser_always_advancing_members_worklist( let mut si = __tco_loop_si; match queue.clone().first().cloned() { Some(name) => { - let rest = Rc::new( - queue - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&queue.clone(), 1)); if set_has(proven.clone(), name.clone()) { { let __tco_0 = rest.clone(); @@ -10220,10 +10201,8 @@ pub fn merge_param_evidence( DescentEvidence::Strict, |acc: DescentEvidence, call_evidence: Rc>>| match call_evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => crate::std_termination::descent_evidence_lattice_meet( acc.clone(), @@ -10246,10 +10225,8 @@ pub fn extract_shrink_factor( std::option::Option::None => std::option::Option::None, Some(prev) => match call_evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => match (*rel.clone()).clone() { SubValueRelation::StrictSubValue { factor: f, .. } => { @@ -10301,10 +10278,8 @@ pub fn max_path_descending( match de.clone() { Some(evidence) => match evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => match (*rel.clone()).clone() { SubValueRelation::StrictSubValue { .. } => 1, @@ -10420,10 +10395,8 @@ pub fn distinct_descended_fields( |acc: Rc>, call_evidence: Rc>>| { match call_evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => match (*rel.clone()).clone() { SubValueRelation::StrictSubValue { field: f, .. } => { diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index 7700517388f..02996da83ab 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -840,16 +840,12 @@ pub fn order_typed_call_args_from_semantics( for app in plan.iter().cloned() { __result.extend( (*match app.matched_argument_index.clone() { - Some(argument_index) => match args - .clone() - .iter() - .cloned() - .skip(argument_index.clone() as usize) - .next() - { - Some(arg) => Rc::new(vec![arg.clone()]), - std::option::Option::None => Rc::new(vec![]), - }, + Some(argument_index) => { + match args.clone().get((argument_index.clone()) as usize).cloned() { + Some(arg) => Rc::new(vec![arg.clone()]), + std::option::Option::None => Rc::new(vec![]), + } + } std::option::Option::None => Rc::new(vec![]), }) .iter() @@ -895,7 +891,7 @@ pub fn has_nested_records_node( source_indices.clone(), ); if is_map.clone() { - match n.children.clone().iter().cloned().skip(1 as usize).next() { + match n.children.clone().get((1) as usize).cloned() { Some(val_child) => { let __tco_0 = crate::v1_compiler_infer_types::child_type_node(val_child.clone()); @@ -1964,29 +1960,26 @@ pub fn render_node_type( } std::option::Option::None => "_".to_string(), }; - let second_child = - match n.children.clone().iter().cloned().skip(1 as usize).next() { - Some(c) => { - if (c.inferred.clone() != std::option::Option::None) { - render_node_type( - crate::v1_compiler_infer_types::resolved_type( - c.clone(), - ), - target.clone(), - shared_types.clone(), - source_indices.clone(), - ) - } else { - render_node_type( - c.clone(), - target.clone(), - shared_types.clone(), - source_indices.clone(), - ) - } + let second_child = match n.children.clone().get((1) as usize).cloned() { + Some(c) => { + if (c.inferred.clone() != std::option::Option::None) { + render_node_type( + crate::v1_compiler_infer_types::resolved_type(c.clone()), + target.clone(), + shared_types.clone(), + source_indices.clone(), + ) + } else { + render_node_type( + c.clone(), + target.clone(), + shared_types.clone(), + source_indices.clone(), + ) } - std::option::Option::None => "_".to_string(), - }; + } + std::option::Option::None => "_".to_string(), + }; let tuple_str = render_tuple_parts( Rc::new(vec![first_child.clone(), second_child.clone()]), target.clone(), @@ -2144,7 +2137,7 @@ pub fn render_node_type( ), std::option::Option::None => "_".to_string(), }; - let v = match n.children.clone().iter().cloned().skip(1 as usize).next() { + let v = match n.children.clone().get((1) as usize).cloned() { Some(vn) => render_node_type( crate::v1_compiler_infer_types::child_type_node(vn.clone()), target.clone(), @@ -2271,7 +2264,7 @@ pub fn render_tuple_parts(parts: Rc>, target: RenderTarget) -> Strin if ((parts.clone().len() as i64) > 0) { if ((parts.clone().len() as i64) == 2) { match parts.clone().first().cloned() { - Some(p0) => match parts.clone().iter().cloned().skip(1 as usize).next() { + Some(p0) => match parts.clone().get((1) as usize).cloned() { Some(p1) => crate::v1_compiler_emit_core_support::apply_type_template2( ts.pair_template.clone(), p0.clone(), @@ -3474,14 +3467,10 @@ pub fn block_stmts_init(stmts: Rc>>) -> Rc>> { if ((stmts.clone().len() as i64) <= 1) { Rc::new(vec![]) } else { - Rc::new( - stmts - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub((stmts.clone().len() as i64), 1) as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_take( + &stmts.clone(), + v1_rt::int_sub((stmts.clone().len() as i64), 1), + )) } } @@ -6816,13 +6805,7 @@ pub fn emit_block_stmts_shared( }; let next_scope = scope_after_expr(stmt.clone(), scope.clone()); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::rc_list_push(text, line.clone()); let __tco_2 = next_scope.clone(); let __tco_3 = depth; @@ -6870,14 +6853,7 @@ pub fn emit_init_block_stmts_shared( }); } Some(stmt) => { - let rest = Rc::new( - remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&remaining.clone(), 1)); match rest.clone().first().cloned() { std::option::Option::None => { break Rc::new(BlockEmitState { @@ -8620,13 +8596,7 @@ pub fn emit_typed_tco_reassign_shared( let mut __result = Vec::new(); for pair in pairs.iter().cloned() { __result.push({ - let av = match arg_values - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + let av = match arg_values.clone().get((pair.0.clone()) as usize).cloned() { Some(v) => v.clone(), std::option::Option::None => pair.1.clone(), }; diff --git a/src/v1/stage0/src/v1_compiler_emit_core_support.rs b/src/v1/stage0/src/v1_compiler_emit_core_support.rs index cd3986c091a..251e9b4f561 100644 --- a/src/v1/stage0/src/v1_compiler_emit_core_support.rs +++ b/src/v1/stage0/src/v1_compiler_emit_core_support.rs @@ -552,13 +552,7 @@ pub fn apply_named_template_nested( match keys.clone().first().cloned() { std::option::Option::None => template.clone(), Some(key) => { - let rest = Rc::new( - keys.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&keys.clone(), 1)); let placeholder = v1_rt::concat(v1_rt::concat("{".to_string(), key.clone()), "}".to_string()); match v1_rt::map_get(&bindings, key.clone()) { diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index e28b0327929..cc6400b2ee9 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -688,14 +688,7 @@ pub fn render_rust_type_without_applied_binding( let key_node = crate::v1_compiler_infer_types::child_type_node( key_child.clone(), ); - let val_node = match n - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let val_node = match n.children.clone().get((1) as usize).cloned() { Some(val_child) => { if rust_type_node_is_arrow(val_child.clone()) { val_child.clone() @@ -2880,7 +2873,7 @@ if peel.clone() { } else { if crate::v1_compiler_infer_types::node_is_keyed_collection(n.clone(), source_indices.clone()) { match arg_list.clone().first().cloned() { - Some(k) => match arg_list.clone().iter().cloned().skip(1 as usize).next() { + Some(k) => match arg_list.clone().get((1) as usize).cloned() { Some(v) => crate::v1_compiler_emit::emit_keyed_container_type(name.clone(), k.clone(), v.clone(), base.clone(), RenderTarget::Rust), std::option::Option::None => v1_rt::concat(v1_rt::concat(v1_rt::concat(base.clone(), "<".to_string()), arg_list.clone().join(&", ".to_string())), ">".to_string()), }, @@ -3295,7 +3288,7 @@ pub fn rust_alias_rhs_applied_container_or_base( ) -> String { if crate::v1_compiler_infer_types::node_is_keyed_collection(n.clone(), source_indices.clone()) { match arg_list.clone().first().cloned() { - Some(k) => match arg_list.clone().iter().cloned().skip(1 as usize).next() { + Some(k) => match arg_list.clone().get((1) as usize).cloned() { Some(v) => crate::v1_compiler_emit::emit_keyed_container_type( leaf.clone(), k.clone(), @@ -5951,13 +5944,7 @@ pub fn emit_rust_block_stmts( let next_scope = crate::v1_compiler_emit::scope_after_expr(stmt.clone(), scope.clone()); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::rc_list_push(text, line.clone()); let __tco_2 = next_scope.clone(); let __tco_3 = registry; @@ -6010,14 +5997,7 @@ pub fn emit_rust_init_block_stmts( }); } Some(stmt) => { - let rest = Rc::new( - remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&remaining.clone(), 1)); match rest.clone().first().cloned() { std::option::Option::None => { break Rc::new(BlockEmitState { @@ -10589,16 +10569,10 @@ pub fn qualified_type_reference_rows( .map(|s| s.to_string()) .collect::>(), ); - let qualifier = Rc::new( - segments - .clone() - .iter() - .cloned() - .take( - v1_rt::int_sub((segments.clone().len() as i64), 1) as usize - ) - .collect::>(), - ) + let qualifier = Rc::new(v1_rt::list_take( + &segments.clone(), + v1_rt::int_sub((segments.clone().len() as i64), 1), + )) .join(&".".to_string()); if ((((qualifier.clone() == this_module_name.clone()) || crate::v1_compiler_infer_types::emit_map_has( @@ -10796,8 +10770,8 @@ pub fn qualified_type_reference_use_lines( .cloned() { __result.extend( - (*Rc::new( - Rc::new({ + (*Rc::new(v1_rt::list_take( + &Rc::new({ let mut __result = Vec::new(); for l in typed.iter().cloned() { if (l.text.clone() == text.clone()) { @@ -10805,12 +10779,9 @@ pub fn qualified_type_reference_use_lines( } } __result - }) - .iter() - .cloned() - .take(1 as usize) - .collect::>(), - )) + }), + 1, + ))) .iter() .cloned(), ); @@ -14667,10 +14638,8 @@ pub fn rust_use_after_crate(line: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(rest) => rest.clone(), std::option::Option::None => "".to_string(), @@ -14815,10 +14784,8 @@ pub fn rust_pub_use_braced_names(line: String) -> Rc> { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(rest) => match Rc::new( rest.clone() @@ -15014,10 +14981,8 @@ pub fn rust_use_bound_symbol(entry: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(bound_name) => bound_name.clone(), std::option::Option::None => entry.clone(), @@ -19013,20 +18978,8 @@ pub fn v1_record_decl_slot_arg_name( ); } else { { - let __tco_0 = Rc::new( - decl_params - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); - let __tco_1 = Rc::new( - type_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&decl_params, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&type_args, 1)); let __tco_2 = slot_name; let __tco_3 = source_indices; __tco_loop_decl_params = __tco_0; @@ -19100,22 +19053,8 @@ pub fn v1_positional_bound_forwarded_names( v1_rt::concat( here.clone(), v1_positional_bound_forwarded_names( - Rc::new( - callee_names - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - wrapper_names - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&callee_names.clone(), 1)), + Rc::new(v1_rt::list_skip(&wrapper_names.clone(), 1)), callee_bound_param_names.clone(), wrapper_generic_param_names.clone(), ), @@ -24775,14 +24714,7 @@ pub fn rust_empty_map_value_type_str( shared_types: Rc>, source_indices: Rc>>, ) -> String { - match map_type - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match map_type.children.clone().get((1) as usize).cloned() { Some(value_child) => { let rendered = render_rust_type( crate::v1_compiler_infer_types::child_type_node(value_child.clone()), @@ -26542,10 +26474,8 @@ pub fn rust_call_arg_fail_closed_unwrap( ) -> String { match callee.clone() { Some(info) => match function_value_params(info.params.clone()) - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(param) => { let param_type = crate::v1_std_core::param_node_type_expr(param.clone()); @@ -26589,10 +26519,8 @@ pub fn rust_call_arg_function_value_adapt( } match callee.clone() { Some(info) => match function_value_params(info.params.clone()) - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(param) => { let arity = (crate::v1_std_core::param_node_type_expr(param.clone()) @@ -26702,14 +26630,7 @@ pub fn lambda_argument_scope(arg: Rc, scope: Rc) -> Rc>(), - ), + Rc::new(v1_rt::list_skip(&arg.children.clone(), 1)), ), _ => scope.clone(), } @@ -26750,7 +26671,7 @@ pub fn emit_typed_call( scope.clone(), ); let get_list = get_args.clone().first().cloned(); - let get_idx = get_args.clone().iter().cloned().skip(1 as usize).next(); + let get_idx = get_args.clone().get((1) as usize).cloned(); let get_result = match get_list.clone() { Some(list_arg) => match get_idx.clone() { Some(idx_arg) => { @@ -26814,10 +26735,8 @@ pub fn emit_typed_call( let update_arg = crate::v1_std_core::arg_value( with_args .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() .clone() .unwrap(), ); @@ -27054,10 +26973,8 @@ pub fn emit_typed_call( let a = pair.1.clone(); let arg_emit_info = match application_plan .clone() - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(application) => { crate::v1_compiler_infer_emit_info::emit_info_with_expected_type( @@ -27511,13 +27428,7 @@ pub fn emit_nested_rt_concat( ) }; { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = next_acc.clone(); let __tco_2 = shared_types; __tco_loop_remaining = __tco_0; @@ -27892,13 +27803,7 @@ pub fn lambda_scope_from_children( .fold(scope.clone(), |acc: Rc, pair: (i64, String)| { let idx = pair.0.clone(); let param_name = pair.1.clone(); - let param_type = match param_nodes - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() - { + let param_type = match param_nodes.clone().get((idx.clone()) as usize).cloned() { Some(pn) => match pn.inferred.clone().as_deref().cloned() { Some(InferredNode::Resolved { node: resolved_type, @@ -27947,13 +27852,7 @@ pub fn lambda_param_type_strs( let inferred_type = if (fold_acc_uses_fallback.clone() && (idx.clone() == 0)) { std::option::Option::None } else { - match param_nodes - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() - { + match param_nodes.clone().get((idx.clone()) as usize).cloned() { Some(pn) => match pn.inferred.clone().as_deref().cloned() { Some(InferredNode::Resolved { node: param_type, .. @@ -28008,16 +27907,11 @@ pub fn lambda_param_type_strs( std::option::Option::None => std::option::Option::None, } }; - let fallback_type = match fallback_types - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() - { - Some(ty) => ty.clone(), - std::option::Option::None => "_".to_string(), - }; + let fallback_type = + match fallback_types.clone().get((idx.clone()) as usize).cloned() { + Some(ty) => ty.clone(), + std::option::Option::None => "_".to_string(), + }; let spec = crate::v1_compiler_emit_core_support::language_spec(RenderTarget::Rust); let ident = crate::v1_compiler_emit::emit_ident(param_name.clone(), RenderTarget::Rust); @@ -28057,15 +27951,7 @@ pub fn emit_typed_collection_lambda( scope.type_env.clone().source_indices.clone(), ); let bd = crate::v1_std_core::lambda_body(lambda_expr.clone()); - let pn = Rc::new( - lambda_expr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let pn = Rc::new(v1_rt::list_skip(&lambda_expr.children.clone(), 1)); let param_strs = lambda_param_type_strs( ps.clone(), pn.clone(), @@ -28136,15 +28022,7 @@ pub fn emit_typed_fold_lambda( scope.type_env.clone().source_indices.clone(), ); let bd = crate::v1_std_core::lambda_body(lambda_expr.clone()); - let pn = Rc::new( - lambda_expr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let pn = Rc::new(v1_rt::list_skip(&lambda_expr.children.clone(), 1)); let safe_acc_type = rust_fold_safe_acc_type(acc_type_str.clone()); let fallback_types = Rc::new({ let mut __result = Vec::new(); @@ -28406,7 +28284,7 @@ pub fn emit_rust_fold_method_call( scope.type_env.clone().source_indices.clone(), acc_type_node.clone(), ); - let fold_lambda_node = match args.clone().iter().cloned().skip(1 as usize).next() { + let fold_lambda_node = match args.clone().get((1) as usize).cloned() { Some(a) => crate::v1_std_core::arg_value(a.clone()), std::option::Option::None => type_variable_node("".to_string()), }; @@ -28700,7 +28578,7 @@ pub fn emit_rust_fold_method_call( let sharing = crate::v1_compiler_emit_core_support::language_spec(RenderTarget::Rust) .sharing .clone(); - let elem_unused = match args.clone().iter().cloned().skip(1 as usize).next() { + let elem_unused = match args.clone().get((1) as usize).cloned() { Some(a) => fold_lambda_element_unused( crate::v1_std_core::arg_value(a.clone()), scope.type_env.clone().source_indices.clone(), @@ -28720,7 +28598,7 @@ pub fn emit_rust_fold_method_call( iter_template.clone(), recv_str.clone(), ); - let step_awaits = match args.clone().iter().cloned().skip(1 as usize).next() { + let step_awaits = match args.clone().get((1) as usize).cloned() { Some(a) => rust_expr_reaches_awaited_call( crate::v1_std_core::arg_value(a.clone()), registry.clone(), @@ -28742,7 +28620,7 @@ pub fn emit_rust_fold_method_call( ) } else { { - let fold_fn = match args.clone().iter().cloned().skip(1 as usize).next() { + let fold_fn = match args.clone().get((1) as usize).cloned() { Some(a) => emit_typed_fold_lambda( crate::v1_std_core::arg_value(a.clone()), lambda_acc_type_str.clone(), @@ -28791,13 +28669,13 @@ pub fn emit_rust_effectful_fold_loop( ExprData::ExprLambda => { let si = scope.type_env.clone().source_indices.clone(); let ps = crate::v1_std_core::lambda_param_names_at(lambda_expr.clone(), si.clone()); -let pn = Rc::new(lambda_expr.children.clone().iter().cloned().skip(1 as usize).collect::>()); +let pn = Rc::new(v1_rt::list_skip(&lambda_expr.children.clone(), 1)); let acc_param_type = rust_fold_safe_acc_type(acc_type_str.clone()); let acc_name = match ps.clone().first().cloned() { Some(n) => n.clone(), std::option::Option::None => "_".to_string(), }; -let elem_name = match ps.clone().iter().cloned().skip(1 as usize).next() { +let elem_name = match ps.clone().get((1) as usize).cloned() { Some(n) => n.clone(), std::option::Option::None => "_".to_string(), }; @@ -28898,7 +28776,7 @@ pub fn fold_lambda_element_unused( lambda_expr.clone(), source_indices.clone(), ); - match ps.clone().iter().cloned().skip(1 as usize).next() { + match ps.clone().get((1) as usize).cloned() { Some(elem_name) => (elem_name.clone() == "_".to_string()), std::option::Option::None => false, } @@ -29111,15 +28989,10 @@ pub fn emit_rust_map_method_call( let lambda_scope = lambda_scope_from_children( scope.clone(), ps.clone(), - Rc::new( - crate::v1_std_core::arg_value(a.clone()) - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &crate::v1_std_core::arg_value(a.clone()).children.clone(), + 1, + )), ); let body_str = emit_typed_expr( bd.clone(), @@ -29210,15 +29083,10 @@ pub fn emit_rust_map_method_call( let lambda_scope = lambda_scope_from_children( scope.clone(), ps.clone(), - Rc::new( - crate::v1_std_core::arg_value(a.clone()) - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &crate::v1_std_core::arg_value(a.clone()).children.clone(), + 1, + )), ); let body_str = emit_typed_expr( bd.clone(), @@ -29334,15 +29202,10 @@ pub fn emit_rust_higher_order_method( let lambda_scope = lambda_scope_from_children( scope.clone(), ps.clone(), - Rc::new( - crate::v1_std_core::arg_value(a.clone()) - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &crate::v1_std_core::arg_value(a.clone()).children.clone(), + 1, + )), ); let body_str = emit_typed_expr( bd.clone(), @@ -29654,10 +29517,10 @@ pub fn emit_rust_first_method_call( ); v1_rt::concat( v1_rt::concat( - v1_rt::concat(recv_str.clone(), ".iter().cloned().skip(".to_string()), + v1_rt::concat(recv_str.clone(), ".get((".to_string()), n_str.clone(), ), - " as usize).next()".to_string(), + ") as usize).cloned()".to_string(), ) } } else { @@ -35487,14 +35350,7 @@ pub fn emit_tco_init_block_stmts( }); } Some(stmt) => { - let rest = Rc::new( - remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&remaining.clone(), 1)); match rest.clone().first().cloned() { std::option::Option::None => { break Rc::new(BlockEmitState { @@ -36679,13 +36535,7 @@ pub fn emit_typed_tco_reassign( let mut __result = Vec::new(); for pair in pairs.iter().cloned() { __result.push({ - let av = match arg_values - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + let av = match arg_values.clone().get((pair.0.clone()) as usize).cloned() { Some(v) => v.clone(), std::option::Option::None => pair.1.clone(), }; @@ -39003,16 +38853,7 @@ pub fn emit_shell_call( let arg_lines = if ((argv.clone().len() as i64) > 1) { Rc::new({ let mut __result = Vec::new(); - for arg in Rc::new( - argv.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - { + for arg in Rc::new(v1_rt::list_skip(&argv.clone(), 1)).iter().cloned() { __result.push( if shell_argv_element_is_word_list( arg.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 1b84f5114e5..de484c14aad 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -481,13 +481,7 @@ pub fn infer_block_stmts( let next_scope = scope_after_stmt_node(stmt.clone(), stmt_rt.clone(), scope.clone()); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::int_sub(remaining_count, 1); let __tco_2 = next_scope.clone(); let __tco_3 = v1_rt::rc_list_push(typed_stmts, stmt_typed.clone()); @@ -638,13 +632,7 @@ Rc::new(InferScopeComponents { } }); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = parent_index; let __tco_2 = env; let __tco_3 = parent_result.svc_registry.clone(); @@ -658,13 +646,7 @@ Rc::new(InferScopeComponents { } } std::option::Option::None => { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = parent_index; let __tco_2 = env; let __tco_3 = svc_registry; @@ -737,10 +719,8 @@ pub fn classify_field_recursion( let value_type = match type_expr .children .clone() - .iter() + .get((value_index.clone()) as usize) .cloned() - .skip(value_index.clone() as usize) - .next() { Some(t) => { crate::v1_std_core::authored_name_at(source_indices.clone(), t.clone()) @@ -2891,7 +2871,7 @@ pub fn record_lit_instantiated_fields( Some(template_fields) => { let subst = Rc::new(decl.params.clone().iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()).iter().cloned().fold(v1_rt::rc_empty_map::>(), |acc: Rc>>, pair: (i64, Rc)| { let slot = crate::v1_std_core::authored_name_at(si.clone(), pair.1.clone()); -match exp.children.clone().iter().cloned().skip(pair.0.clone() as usize).next() { +match exp.children.clone().get((pair.0.clone()) as usize).cloned() { Some(arg) => v1_rt::rc_map_insert(acc.clone(), slot.clone(), crate::v1_compiler_infer_types::child_type_node(arg.clone())), std::option::Option::None => acc.clone(), } @@ -4414,16 +4394,9 @@ pub fn list_literal_optional_member_diags( let head_type = crate::v1_compiler_infer_types::resolved_type(head.clone()); Rc::new({ let mut __result = Vec::new(); - for te in Rc::new( - typed_elements - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() + for te in Rc::new(v1_rt::list_skip(&typed_elements.clone(), 1)) + .iter() + .cloned() { __result.extend( (*optional_at_required_obligation_diags( @@ -6926,20 +6899,8 @@ pub fn coproduct_applied_type_arguments_conflict_scan( break true; } else { { - let __tco_0 = Rc::new( - declared_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); - let __tco_1 = Rc::new( - produced_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&declared_args, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&produced_args, 1)); let __tco_2 = scope; __tco_loop_declared_args = __tco_0; __tco_loop_produced_args = __tco_1; @@ -7079,20 +7040,8 @@ pub fn applied_type_arguments_conflict_scan( break true; } else { { - let __tco_0 = Rc::new( - declared_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); - let __tco_1 = Rc::new( - produced_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&declared_args, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&produced_args, 1)); let __tco_2 = scope; __tco_loop_declared_args = __tco_0; __tco_loop_produced_args = __tco_1; @@ -7970,14 +7919,14 @@ pub fn call_argument_formal_at_position( } __result }) - .iter() + .get( + (call_argument_positional_rank( + arguments.clone(), + argument_index.clone(), + source_indices.clone(), + )) as usize, + ) .cloned() - .skip(call_argument_positional_rank( - arguments.clone(), - argument_index.clone(), - source_indices.clone(), - ) as usize) - .next() { Some(positional) => Rc::new(CallArgumentFormalSelection::CallArgumentFormalSelected { formal_index: positional.0.clone(), @@ -8106,13 +8055,7 @@ pub fn build_call_application_plan( .cloned() { __result.extend( - (*match formals - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + (*match formals.clone().get((pair.0.clone()) as usize).cloned() { Some(carried) => Rc::new(vec![Rc::new(ResolvedCallFormal { formal_index: pair.0.clone(), formal: Rc::new(ResolvedFormal { @@ -8190,7 +8133,7 @@ pub fn direct_call_structured_application_mismatch_diags( let formal_subst = app.formal.clone().declared_type.clone(); let formal = app.formal.clone().declaration_bound_conformance.clone(); match app.matched_argument_index.clone() { - Some(arg_index) => match typed_args.clone().iter().cloned().skip(arg_index.clone() as usize).next() { + Some(arg_index) => match typed_args.clone().get((arg_index.clone()) as usize).cloned() { Some(ta) => { let actual_expr = crate::v1_std_core::arg_value(ta.clone()); let host_into_sequence = (!type_node_is_callable(formal.clone()) && crate::v1_compiler_infer_env::host_text_into_structural_sequence(formal.clone(), crate::v1_compiler_infer_resolve::peel_nominal_alias_identity(crate::v1_compiler_infer_types::resolved_type(actual_expr.clone()), type_env.clone(), module_name.clone()), crate::v1_compiler_infer_types::resolved_type(actual_expr.clone()), type_env.clone())); @@ -8274,7 +8217,7 @@ pub fn sibling_effect_order_diags( Some(x) => x.clone(), std::option::Option::None => "".to_string(), }; - let b = match effectful.clone().iter().cloned().skip(1 as usize).next() { + let b = match effectful.clone().get((1) as usize).cloned() { Some(x) => x.clone(), std::option::Option::None => "".to_string(), }; @@ -8338,10 +8281,8 @@ pub fn direct_call_argument_inhabitance_diags( (*match app.matched_argument_index.clone() { Some(arg_index) => match typed_args .clone() - .iter() + .get((arg_index.clone()) as usize) .cloned() - .skip(arg_index.clone() as usize) - .next() { Some(ta) => { if type_node_is_callable(app.formal.clone().declared_type.clone()) { @@ -8395,13 +8336,13 @@ pub fn direct_call_generic_type_argument_inhabitance_diags( let mut __result = Vec::new(); for app in plan.iter().cloned() { __result.extend((*match app.matched_argument_index.clone() { - Some(arg_index) => match typed_args.clone().iter().cloned().skip(arg_index.clone() as usize).next() { + Some(arg_index) => match typed_args.clone().get((arg_index.clone()) as usize).cloned() { Some(ta) => { let actual_expr = crate::v1_std_core::arg_value(ta.clone()); let actual = crate::v1_compiler_infer_types::resolved_type(actual_expr.clone()); if ((((exposure_is_application(exposure_view_for_node(app.formal.clone().declared_type.clone(), source_indices.clone())) && exposure_is_application(exposure_view_for_node(actual.clone(), source_indices.clone()))) && ((app.formal.clone().declared_type.clone().children.clone().len() as i64) > 0)) && ((app.formal.clone().declared_type.clone().children.clone().len() as i64) == (actual.children.clone().len() as i64))) && (crate::v1_std_core::authored_name_at(source_indices.clone(), app.formal.clone().declared_type.clone()) == crate::v1_std_core::authored_name_at(source_indices.clone(), actual.clone()))) { - Rc::new({ let mut __result = Vec::new(); for declared_pair in Rc::new(app.formal.clone().declared_type.clone().children.clone().iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()).iter().cloned() { __result.extend((*match app.formal.clone().substitution_basis.clone().children.clone().iter().cloned().skip(declared_pair.0.clone() as usize).next() { - Some(raw_declared_child) => match actual.children.clone().iter().cloned().skip(declared_pair.0.clone() as usize).next() { + Rc::new({ let mut __result = Vec::new(); for declared_pair in Rc::new(app.formal.clone().declared_type.clone().children.clone().iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()).iter().cloned() { __result.extend((*match app.formal.clone().substitution_basis.clone().children.clone().get((declared_pair.0.clone()) as usize).cloned() { + Some(raw_declared_child) => match actual.children.clone().get((declared_pair.0.clone()) as usize).cloned() { Some(produced_child) => if (applied_type_argument_identity_known(crate::v1_std_core::authored_name_at(source_indices.clone(), crate::v1_compiler_infer_types::child_type_node(raw_declared_child.clone())), scope.clone()) && applied_type_argument_identity_known(crate::v1_std_core::authored_name_at(source_indices.clone(), crate::v1_compiler_infer_types::child_type_node(produced_child.clone())), scope.clone())) { declared_type_obligation_diags(Rc::new(DeclaredTypeObligation { position: DeclaredTypePosition::PositionGenericTypeArgument, @@ -8615,10 +8556,8 @@ pub fn callable_signature_substitution( acc.clone(), |st: Rc>>, pair: (i64, Rc)| match a_params .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(ap) => callable_leaf_substitution( callable_param_type(pair.1.clone()), @@ -8739,13 +8678,7 @@ pub fn callable_arrow_mismatch( .iter() .cloned() { - if match a_params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + if match a_params.clone().get((pair.0.clone()) as usize).cloned() { Some(ap) => callable_component_ground_mismatch( callable_param_type(pair.1.clone()), callable_param_type(ap.clone()), @@ -8830,16 +8763,9 @@ pub fn expression_value_type(e: Rc) -> Rc { resolved_callable_type( Rc::new({ let mut __result = Vec::new(); - for lp in Rc::new( - e.children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() + for lp in Rc::new(v1_rt::list_skip(&e.children.clone(), 1)) + .iter() + .cloned() { __result.push(lambda_arrow_param(lp.clone())); } @@ -9068,12 +8994,9 @@ pub fn borrowed_callable_call_type( .cloned() { Some(named_ta) => Some(named_ta.clone()), - std::option::Option::None => typed_args - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next(), + std::option::Option::None => { + typed_args.clone().get((pair.0.clone()) as usize).cloned() + } }; match matched_arg.clone() { Some(ta) => unify_generics( @@ -9158,14 +9081,10 @@ pub fn call_arg_bound_param_at( source_indices: Rc>>, positionally_eligible_names: Rc>, ) -> Option { - Rc::new( - typed_args - .clone() - .iter() - .cloned() - .take(v1_rt::int_add(idx.clone(), 1) as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_take( + &typed_args.clone(), + v1_rt::int_add(idx.clone(), 1), + )) .iter() .cloned() .fold( @@ -9184,10 +9103,8 @@ pub fn call_arg_bound_param_at( std::option::Option::None => Rc::new(CallArgBoundParamAccum { bound: positionally_eligible_names .clone() - .iter() - .cloned() - .skip(acc.pos.clone() as usize) - .next(), + .get((acc.pos.clone()) as usize) + .cloned(), pos: v1_rt::int_add(acc.pos.clone(), 1), }), }, @@ -9406,10 +9323,8 @@ pub fn direct_call_shape_diags( if ((positional_args.clone().len() as i64) > positional_capacity.clone()) { match positional_args .clone() - .iter() + .get((positional_capacity.clone()) as usize) .cloned() - .skip(positional_capacity.clone() as usize) - .next() { Some(overflow) => Rc::new(vec![crate::v1_std_core::make_error_node( Rc::new(CompilerDiagnostic::CallPositionalSurplus { @@ -9613,10 +9528,8 @@ pub fn function_value_call_named_arg_diags( if (supplied.clone() > param_count.clone()) { match typed_args .clone() - .iter() + .get((param_count.clone()) as usize) .cloned() - .skip(param_count.clone() as usize) - .next() { Some(overflow) => Rc::new(vec![crate::v1_std_core::make_error_node( Rc::new(CompilerDiagnostic::CallPositionalSurplus { @@ -9715,7 +9628,7 @@ pub fn direct_call_arg_mismatch_diags( __result.extend((*{ let formal = app.formal.clone().declaration_bound_conformance.clone(); match app.matched_argument_index.clone() { - Some(arg_index) => match typed_args.clone().iter().cloned().skip(arg_index.clone() as usize).next() { + Some(arg_index) => match typed_args.clone().get((arg_index.clone()) as usize).cloned() { Some(ta) => { let actual_expr = crate::v1_std_core::arg_value(ta.clone()); if match (*actual_expr.expr_data.clone()).clone() { @@ -9802,7 +9715,7 @@ pub fn builtin_text_argument_crossing_diags( let arg = pair.1.clone(); let bound_name = match crate::v1_std_core::arg_name_at(arg.clone(), scope.type_env.clone().source_indices.clone()) { Some(label) => label.clone(), - std::option::Option::None => match formal_names.clone().iter().cloned().skip(pair.0.clone() as usize).next() { + std::option::Option::None => match formal_names.clone().get((pair.0.clone()) as usize).cloned() { Some(n) => n.clone(), std::option::Option::None => "".to_string(), }, @@ -10019,7 +9932,7 @@ pub fn infer_tier2b_builtin_with_kernel_diags( let operand_elem = if ((func_name.clone() == "set_insert".to_string()) || (func_name.clone() == "set_contains".to_string())) { - match typed_args.clone().iter().cloned().skip(1 as usize).next() { + match typed_args.clone().get((1) as usize).cloned() { Some(insert_arg) => { match crate::v1_std_core::arg_value(insert_arg.clone()) .inferred @@ -10036,7 +9949,7 @@ pub fn infer_tier2b_builtin_with_kernel_diags( std::option::Option::None => std::option::Option::None, } } else { - match typed_args.clone().iter().cloned().skip(1 as usize).next() { + match typed_args.clone().get((1) as usize).cloned() { Some(other_arg) => match crate::v1_std_core::arg_value(other_arg.clone()).inferred.clone().as_deref().cloned() { Some(InferredNode::Resolved { node: other_type, .. }) => match crate::v1_compiler_infer_lookup::set_element_type_in_env(other_type.clone(), scope.type_env.clone()) { Some(elem_slot) => Some(crate::v1_compiler_infer_types::child_type_node(elem_slot.clone())), @@ -10055,7 +9968,7 @@ pub fn infer_tier2b_builtin_with_kernel_diags( }; let union_other_bad = if (func_name.clone() == "set_union".to_string()) { - match typed_args.clone().iter().cloned().skip(1 as usize).next() { + match typed_args.clone().get((1) as usize).cloned() { Some(other_arg) => set_union_other_operand_is_resolved_non_set( other_arg.clone(), scope.clone(), @@ -11743,7 +11656,7 @@ Rc::new(ArgInferResult { diagnostics: v1_rt::concat(ar.diagnostics.clone(), Rc::new(vec![contract_diag.clone()])), }) }, - DeclaredArgContract::ContractKnown { types: declared_types, .. } => match declared_types.clone().iter().cloned().skip(idx.clone() as usize).next() { + DeclaredArgContract::ContractKnown { types: declared_types, .. } => match declared_types.clone().get((idx.clone()) as usize).cloned() { Some(declared_type) => infer_arg_with_element_type(a.clone(), declared_type.clone(), nf_scope.clone()), std::option::Option::None => { let ar = infer_expr(nf_lam_value.clone(), nf_scope.clone(), std::option::Option::None); @@ -12006,10 +11919,8 @@ pub fn infer_call_arguments_generic_pass( formal_index, .. } => match formals .clone() - .iter() + .get((formal_index.clone()) as usize) .cloned() - .skip(formal_index.clone() as usize) - .next() { Some(carried) => carried.substitution_basis.clone(), std::option::Option::None => error_type(), @@ -12040,10 +11951,8 @@ pub fn infer_call_arguments_generic_pass( formal_index, .. } => match formals .clone() - .iter() + .get((formal_index.clone()) as usize) .cloned() - .skip(formal_index.clone() as usize) - .next() { Some(carried) => substitute_generics( carried.declaration_bound_conformance.clone(), @@ -12061,10 +11970,8 @@ pub fn infer_call_arguments_generic_pass( formal_index, .. } => match formals .clone() - .iter() + .get((formal_index.clone()) as usize) .cloned() - .skip(formal_index.clone() as usize) - .next() { Some(carried) => { formal_code_point_view(carried.clone(), scope.type_env.clone()) @@ -13304,14 +13211,7 @@ Rc::new(InferResult { } __found }; - let call_method_args = Rc::new( - call_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let call_method_args = Rc::new(v1_rt::list_skip(&call_args.clone(), 1)); let call_method_name = Some(func_name.clone()); let call_fold_info = extract_fold_init_info( call_method_name.clone(), @@ -13739,14 +13639,8 @@ Rc::new(InferResult { { { let receiver = method_receiver.clone(); - let remaining = Rc::new( - typed_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let remaining = + Rc::new(v1_rt::list_skip(&typed_args.clone(), 1)); let rest_result = rest_operation_result_type( method_resolution.clone(), texpr.span.clone(), @@ -14613,23 +14507,16 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), }, }; let unified_arm_type = match arm_body_types.clone().first().cloned() { - Some(first_type) => Rc::new( - arm_body_types - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold(first_type.clone(), |acc: _, t: Rc| { - crate::v1_compiler_infer_types::prefer_specific_type( - acc, - t.clone(), - scope.type_env.clone().source_indices.clone(), - ) - }), + Some(first_type) => Rc::new(v1_rt::list_skip(&arm_body_types.clone(), 1)) + .iter() + .cloned() + .fold(first_type.clone(), |acc: _, t: Rc| { + crate::v1_compiler_infer_types::prefer_specific_type( + acc, + t.clone(), + scope.type_env.clone().source_indices.clone(), + ) + }), std::option::Option::None => scrut_rt.clone(), }; let arm_infer_results = if crate::v1_compiler_infer_types::is_fully_resolved( @@ -15411,15 +15298,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), texpr.clone(), scope.type_env.clone().source_indices.clone(), ); - let lam_param_nodes = Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let lam_param_nodes = Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)); let elem_prov = match v1_rt::map_get(&scope.lambda_param_provenance.clone(), "elem".to_string()) { Some(p) => p.clone(), @@ -15450,14 +15329,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), } else { Rc::new(SubValueRelation::SubValueUnknown) }; - match exp - .params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + match exp.params.clone().get((pair.0.clone()) as usize).cloned() { Some(cp) => { let cpt = crate::v1_std_core::param_node_type_expr(cp.clone()); @@ -15615,16 +15487,11 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), { __result.push({ let pn = pair.1.clone(); - let param_name = match lam_params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { - Some(n) => n.clone(), - std::option::Option::None => "".to_string(), - }; + let param_name = + match lam_params.clone().get((pair.0.clone()) as usize).cloned() { + Some(n) => n.clone(), + std::option::Option::None => "".to_string(), + }; let param_type = match v1_rt::map_get(&lam_scope.locals.clone(), param_name.clone()) { Some(binding) => Some(Rc::new(InferredNode::Resolved { @@ -16529,13 +16396,7 @@ pub fn alias_chain_type_arg_subst( pair.1.clone(), env.source_indices.clone(), ); - match type_args - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + match type_args.clone().get((pair.0.clone()) as usize).cloned() { Some(arg) => v1_rt::rc_map_insert( acc.clone(), slot.clone(), @@ -17034,14 +16895,7 @@ pub fn infer_map_literal( scope: Rc, ) -> Rc { { - let value_expected = match map_node - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let value_expected = match map_node.children.clone().get((1) as usize).cloned() { Some(v) => Some(v.clone()), std::option::Option::None => std::option::Option::None, }; @@ -18555,10 +18409,8 @@ pub fn maybe_insert_output_field_relation( let composed = match sig .output_provenance .clone() - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(param_map) => { classify_call_via_provenance(val.clone(), param_map.clone(), ctx.clone()) @@ -19549,21 +19401,15 @@ pub fn classify_call_arg_provenance( pub fn merge_argument_relations(rels: Rc>>) -> Rc { match rels.clone().first().cloned() { std::option::Option::None => Rc::new(SubValueRelation::SubValueUnknown), - Some(first_rel) => Rc::new( - rels.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - first_rel.clone(), - |acc: Rc, rel: Rc| { - crate::std_induction::meet_sub_value(acc, rel.clone()) - }, - ), + Some(first_rel) => Rc::new(v1_rt::list_skip(&rels.clone(), 1)) + .iter() + .cloned() + .fold( + first_rel.clone(), + |acc: Rc, rel: Rc| { + crate::std_induction::meet_sub_value(acc, rel.clone()) + }, + ), } } @@ -20112,10 +19958,8 @@ pub fn build_call_evidence( } std::option::Option::None => match positional_args .clone() - .iter() + .get((def_idx.clone()) as usize) .cloned() - .skip(def_idx.clone() as usize) - .next() { Some(arg_val) => { read_arg_provenance(arg_val.clone(), pname.clone(), ctx.clone()) @@ -20273,7 +20117,7 @@ let p1 = match lparams.clone().first().cloned() { Some(n) => n.clone(), std::option::Option::None => "".to_string(), }; -let p2 = match lparams.clone().iter().cloned().skip(1 as usize).next() { +let p2 = match lparams.clone().get((1) as usize).cloned() { Some(n) => n.clone(), std::option::Option::None => "".to_string(), }; @@ -21027,10 +20871,8 @@ Rc::new(BlockAnnotateAcc { ); lparams .clone() - .iter() + .get((elem_position.clone().unwrap()) as usize) .cloned() - .skip(elem_position.clone().unwrap() as usize) - .next() } std::option::Option::None => std::option::Option::None, }; @@ -22118,12 +21960,7 @@ pub fn meet_per_field_results( let mut __result = Vec::new(); for r in results.iter().cloned() { __result.push( - match r - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() + match r.clone().get((idx.clone()) as usize).cloned() { Some(m) => m.clone(), std::option::Option::None => empty_prov_map(), @@ -22153,23 +21990,16 @@ pub fn meet_body_provenance_maps( ) -> Rc>> { match provs.clone().first().cloned() { std::option::Option::None => empty_prov_map(), - Some(first_prov) => Rc::new( - provs - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - first_prov.clone(), - |acc: Rc>>, - prov: Rc>>| { - intersect_prov_maps(acc, prov.clone()) - }, - ), + Some(first_prov) => Rc::new(v1_rt::list_skip(&provs.clone(), 1)) + .iter() + .cloned() + .fold( + first_prov.clone(), + |acc: Rc>>, + prov: Rc>>| { + intersect_prov_maps(acc, prov.clone()) + }, + ), } } @@ -22655,14 +22485,10 @@ if ((Rc::new(v1_rt::map_keys(&composed_field_map)).len() as i64) > 0) { acc.clone() } else { { - let final_lp = Rc::new( - stmts - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub(stmt_count.clone(), 1) as usize) - .collect::>(), - ) + let final_lp = Rc::new(v1_rt::list_take( + &stmts.clone(), + v1_rt::int_sub(stmt_count.clone(), 1), + )) .iter() .cloned() .fold( @@ -22693,10 +22519,8 @@ if ((Rc::new(v1_rt::map_keys(&composed_field_map)).len() as i64) > 0) { ); match stmts .clone() - .iter() + .get((v1_rt::int_sub(stmt_count.clone(), 1)) as usize) .cloned() - .skip(v1_rt::int_sub(stmt_count.clone(), 1) as usize) - .next() { Some(last) => collect_variant_constructors( last.clone(), @@ -24007,15 +23831,7 @@ pub fn unify_callable_generics( pub fn lambda_callable_type(lambda: Rc) -> Rc { crate::v1_compiler_infer_types::make_callable_type( - Rc::new( - lambda - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&lambda.children.clone(), 1)), crate::v1_compiler_infer_types::resolved_type(lambda.clone()), ) } @@ -24041,10 +23857,8 @@ pub fn unify_callable_param_pairs( match formals.clone().first().cloned() { Some(f) => match actuals.clone().first().cloned() { Some(a) => { - let __tco_0 = - Rc::new(formals.iter().cloned().skip(1 as usize).collect::>()); - let __tco_1 = - Rc::new(actuals.iter().cloned().skip(1 as usize).collect::>()); + let __tco_0 = Rc::new(v1_rt::list_skip(&formals, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&actuals, 1)); let __tco_2 = generic_names.clone(); let __tco_3 = source_indices.clone(); let __tco_4 = unify_generics( @@ -24548,30 +24362,23 @@ pub fn union_parent_type_env_caches( cache: crate::v1_compiler_infer_env::empty_type_env_cache(), conflicts: Rc::new(vec![]), }), - Some(head) => Rc::new( - parent_caches - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - Rc::new(GuardedTypeEnvCacheMerge { - cache: head.cache.clone(), - conflicts: Rc::new(vec![]), - }), - |acc: Rc, row: Rc| { - crate::v1_compiler_infer_env::merge_type_env_cache_guarded( - acc.cache.clone(), - row.cache.clone(), - row.import_path.clone(), - acc.conflicts.clone(), - ) - }, - ), + Some(head) => Rc::new(v1_rt::list_skip(&parent_caches.clone(), 1)) + .iter() + .cloned() + .fold( + Rc::new(GuardedTypeEnvCacheMerge { + cache: head.cache.clone(), + conflicts: Rc::new(vec![]), + }), + |acc: Rc, row: Rc| { + crate::v1_compiler_infer_env::merge_type_env_cache_guarded( + acc.cache.clone(), + row.cache.clone(), + row.import_path.clone(), + acc.conflicts.clone(), + ) + }, + ), } } } @@ -27547,25 +27354,18 @@ pub fn build_type_env( source_indices.clone(), ); let parent_inductive_fields = match scope_parents.clone().first().cloned() { - Some(head_parent) => Rc::new( - scope_parents - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - head_parent.inductive_fields.clone(), - |acc: Rc>>>>, parent: Rc| { - crate::v1_compiler_infer_env::merge_inductive_fields( - acc, - parent.inductive_fields.clone(), - ) - }, - ), + Some(head_parent) => Rc::new(v1_rt::list_skip(&scope_parents.clone(), 1)) + .iter() + .cloned() + .fold( + head_parent.inductive_fields.clone(), + |acc: Rc>>>>, parent: Rc| { + crate::v1_compiler_infer_env::merge_inductive_fields( + acc, + parent.inductive_fields.clone(), + ) + }, + ), std::option::Option::None => { v1_rt::rc_empty_map::>>>() } @@ -28161,25 +27961,18 @@ pub fn build_type_env_unresolved( source_indices.clone(), ); let parent_inductive_fields = match scope_parents.clone().first().cloned() { - Some(head_parent) => Rc::new( - scope_parents - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - head_parent.inductive_fields.clone(), - |acc: Rc>>>>, parent: Rc| { - crate::v1_compiler_infer_env::merge_inductive_fields( - acc, - parent.inductive_fields.clone(), - ) - }, - ), + Some(head_parent) => Rc::new(v1_rt::list_skip(&scope_parents.clone(), 1)) + .iter() + .cloned() + .fold( + head_parent.inductive_fields.clone(), + |acc: Rc>>>>, parent: Rc| { + crate::v1_compiler_infer_env::merge_inductive_fields( + acc, + parent.inductive_fields.clone(), + ) + }, + ), std::option::Option::None => { v1_rt::rc_empty_map::>>>() } @@ -28511,13 +28304,7 @@ pub fn fold_module_contributions( std::option::Option::None => svc_locals.clone(), }; { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::rc_list_push(resolved_items, contribution.resolved_item.clone()); let __tco_2 = next_func_sigs.clone(); diff --git a/src/v1/stage0/src/v1_compiler_infer_access.rs b/src/v1/stage0/src/v1_compiler_infer_access.rs index 135f7d12523..c22068e6c1a 100644 --- a/src/v1/stage0/src/v1_compiler_infer_access.rs +++ b/src/v1/stage0/src/v1_compiler_infer_access.rs @@ -86,7 +86,7 @@ pub fn keyed_collection_parts( && ((n.children.clone().len() as i64) >= 2)) { match n.children.clone().first().cloned() { - Some(key_child) => match n.children.clone().iter().cloned().skip(1 as usize).next() { + Some(key_child) => match n.children.clone().get((1) as usize).cloned() { Some(value_child) => Some(Rc::new(KeyedCollectionParts { key_type: crate::v1_compiler_infer_types::resolved_type(key_child.clone()), value_type: crate::v1_compiler_infer_types::resolved_type(value_child.clone()), diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index 3732f70645d..f6b0a8d5201 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -1144,14 +1144,7 @@ pub fn segment_lcp_len(a: Rc>, b: Rc>) -> i64 { Some(bh) => { if (bh.clone() == seg.clone()) { Rc::new(SegmentLcpScan { - remaining: Rc::new( - acc.remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + remaining: Rc::new(v1_rt::list_skip(&acc.remaining.clone(), 1)), matched: v1_rt::int_add(acc.matched.clone(), 1), live: true, }) @@ -1635,13 +1628,10 @@ pub fn qualified_all_but_last(name: String) -> String { "".to_string() } else { { - let scan = Rc::new( - segs.clone() - .iter() - .cloned() - .take(v1_rt::int_sub(seg_count.clone(), 1) as usize) - .collect::>(), - ) + let scan = Rc::new(v1_rt::list_take( + &segs.clone(), + v1_rt::int_sub(seg_count.clone(), 1), + )) .iter() .cloned() .fold("".to_string(), |acc: String, seg: String| { diff --git a/src/v1/stage0/src/v1_compiler_infer_lookup.rs b/src/v1/stage0/src/v1_compiler_infer_lookup.rs index 2dc2196ddc6..f35b3584317 100644 --- a/src/v1/stage0/src/v1_compiler_infer_lookup.rs +++ b/src/v1/stage0/src/v1_compiler_infer_lookup.rs @@ -1003,10 +1003,8 @@ pub fn lookup_field_type_node( let value_child = match n .children .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(child) => { crate::v1_compiler_infer_types::child_type_node( @@ -1272,14 +1270,7 @@ pub fn map_value_type_in_env(type_node: Rc, env: Rc) -> Option= 2)) { - match map_type - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match map_type.children.clone().get((1) as usize).cloned() { Some(value_type) => Some(value_type.clone()), std::option::Option::None => std::option::Option::None, } @@ -1641,14 +1632,7 @@ pub fn declared_arg_types_for_method( std::option::Option::None => false, }; let non_receiver_templates = if first_is_self.clone() { - Rc::new( - t.param_types - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&t.param_types.clone(), 1)) } else { t.param_types.clone() }; diff --git a/src/v1/stage0/src/v1_compiler_infer_patterns.rs b/src/v1/stage0/src/v1_compiler_infer_patterns.rs index e2ab2807ad2..5e999c27c36 100644 --- a/src/v1/stage0/src/v1_compiler_infer_patterns.rs +++ b/src/v1/stage0/src/v1_compiler_infer_patterns.rs @@ -75,10 +75,8 @@ pub fn generic_use_slot_bindings( match scrut_node .children .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(arg) => v1_rt::rc_map_insert(acc.clone(), slot.clone(), arg.clone()), std::option::Option::None => acc.clone(), @@ -1065,13 +1063,7 @@ pub fn specialize_pattern_row( ) -> Rc>>>> { { let head = pattern_row_head(row.clone()); - let rest = Rc::new( - row.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&row.clone(), 1)); if crate::v1_std_core::match_pattern_is_irrefutable(head.clone()) { Rc::new(vec![v1_rt::concat( Rc::new({ @@ -1142,10 +1134,8 @@ pub fn render_constructor_witness( Rc::new(vec![]), |acc: _, pair: (i64, Rc)| match cells .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(cell) => { if (cell.clone() == "_".to_string()) { @@ -1210,13 +1200,7 @@ pub fn default_pattern_rows( .iter() .cloned() { - __result.push(Rc::new( - r.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - )); + __result.push(Rc::new(v1_rt::list_skip(&r.clone(), 1))); } __result }) @@ -1240,14 +1224,7 @@ pub fn exhaustiveness_witnesses( } } Some(head_type) => { - let rest_columns = Rc::new( - columns - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest_columns = Rc::new(v1_rt::list_skip(&columns.clone(), 1)); match (*constructor_roster_for(head_type.clone(), env.clone())).clone() { ConstructorRoster::ConstructorsOpen => { let defaulted = default_pattern_rows(rows.clone()); @@ -1376,24 +1353,15 @@ pub fn exhaustiveness_witnesses( Rc::new(vec![render_constructor_witness( c.clone(), fields.clone(), - Rc::new( - w.cells - .clone() - .iter() - .cloned() - .take(field_count.clone() - as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_take( + &w.cells.clone(), + field_count.clone(), + )), )]), - Rc::new( - w.cells - .clone() - .iter() - .cloned() - .skip(field_count.clone() as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &w.cells.clone(), + field_count.clone(), + )), ), })); } diff --git a/src/v1/stage0/src/v1_compiler_infer_resolve.rs b/src/v1/stage0/src/v1_compiler_infer_resolve.rs index 868d3fc403a..af5f2911654 100644 --- a/src/v1/stage0/src/v1_compiler_infer_resolve.rs +++ b/src/v1/stage0/src/v1_compiler_infer_resolve.rs @@ -101,7 +101,7 @@ pub fn type_param_kind_diagnostics( { __result.extend((*match crate::v1_std_core::find_property(pair.1.clone().properties.clone(), crate::v1_std_core::type_param_kind_property_name(), env.source_indices.clone()) { std::option::Option::None => Rc::new(vec![]), - Some(kind_node) => match carrier.children.clone().iter().cloned().skip(pair.0.clone() as usize).next() { + Some(kind_node) => match carrier.children.clone().get((pair.0.clone()) as usize).cloned() { std::option::Option::None => Rc::new(vec![]), Some(arg) => { let kind_name = crate::v1_compiler_infer_env::authored_name(env.clone(), kind_node.clone()); @@ -1775,17 +1775,11 @@ Rc::new(NodeResolveResult { Some(k) => k.clone(), std::option::Option::None => unit_type(), }; - let val_child_node = match n - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { - Some(v) => v.clone(), - std::option::Option::None => unit_type(), - }; + let val_child_node = + match n.children.clone().get((1) as usize).cloned() { + Some(v) => v.clone(), + std::option::Option::None => unit_type(), + }; let key_type = crate::v1_compiler_infer_types::child_type_node( key_child_node.clone(), ); @@ -3034,25 +3028,20 @@ pub fn resolve_expr_types( }) } }; - let body_r = match arm_ch - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { - Some(b) => resolve_expr_types( - b.clone(), - env.clone(), - module_name.clone(), - ), - std::option::Option::None => { - Rc::new(ExprResolveResult { - expr: child.clone(), - diagnostics: Rc::new(vec![]), - }) - } - }; + let body_r = + match arm_ch.clone().get((1) as usize).cloned() { + Some(b) => resolve_expr_types( + b.clone(), + env.clone(), + module_name.clone(), + ), + std::option::Option::None => { + Rc::new(ExprResolveResult { + expr: child.clone(), + diagnostics: Rc::new(vec![]), + }) + } + }; Rc::new(ExprResolveResult { expr: crate::v1_std_core::make_arm_node( child.occurrence_identity.clone(), @@ -3144,14 +3133,14 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let tr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let tr = match ch.clone().get((1) as usize).cloned() { Some(t) => resolve_expr_types(t.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { expr: texpr.clone(), diagnostics: Rc::new(vec![]), }), }; - let er = match ch.clone().iter().cloned().skip(2 as usize).next() { + let er = match ch.clone().get((2) as usize).cloned() { Some(e) => Some(resolve_expr_types( e.clone(), env.clone(), @@ -3189,7 +3178,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let br = match ch.clone().iter().cloned().skip(1 as usize).next() { + let br = match ch.clone().get((1) as usize).cloned() { Some(bd) => Some(resolve_expr_types( bd.clone(), env.clone(), @@ -3395,7 +3384,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let rr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let rr = match ch.clone().get((1) as usize).cloned() { Some(r) => resolve_expr_types(r.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { expr: texpr.clone(), @@ -3437,15 +3426,7 @@ pub fn resolve_expr_types( }) } ExprData::ExprLambda => { - let lam_param_nodes = Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let lam_param_nodes = Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)); let r = match texpr.children.clone().first().cloned() { Some(b) => resolve_expr_types(b.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { @@ -3571,7 +3552,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let tr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let tr = match ch.clone().get((1) as usize).cloned() { Some(target) => resolve_node(target.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(NodeResolveResult { resolved: unit_type(), @@ -3598,7 +3579,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let br = match ch.clone().iter().cloned().skip(1 as usize).next() { + let br = match ch.clone().get((1) as usize).cloned() { Some(b) => resolve_expr_types(b.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { expr: texpr.clone(), @@ -3632,7 +3613,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let ir = match ch.clone().iter().cloned().skip(1 as usize).next() { + let ir = match ch.clone().get((1) as usize).cloned() { Some(index) => { resolve_expr_types(index.clone(), env.clone(), module_name.clone()) } @@ -3663,7 +3644,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let sr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let sr = match ch.clone().get((1) as usize).cloned() { Some(start) => { resolve_expr_types(start.clone(), env.clone(), module_name.clone()) } @@ -3672,7 +3653,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let er = match ch.clone().iter().cloned().skip(2 as usize).next() { + let er = match ch.clone().get((2) as usize).cloned() { Some(end_e) => { resolve_expr_types(end_e.clone(), env.clone(), module_name.clone()) } @@ -3775,14 +3756,10 @@ pub fn first_duplicate_type_param_name(names: Rc>) -> Option { if { let mut __found = false; - for other in Rc::new( - names - .clone() - .iter() - .cloned() - .skip(v1_rt::int_add(pair.0.clone(), 1) as usize) - .collect::>(), - ) + for other in Rc::new(v1_rt::list_skip( + &names.clone(), + v1_rt::int_add(pair.0.clone(), 1), + )) .iter() .cloned() { @@ -3968,15 +3945,7 @@ pub fn reference_param_with_declaration( binders.clone(), env.clone(), )]), - Rc::new( - param - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(¶m.children.clone(), 1)), ), param.params.clone(), param.inferred.clone(), @@ -4150,15 +4119,7 @@ pub fn binder_marked_param( binders.clone(), env.clone(), )]), - Rc::new( - param - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(¶m.children.clone(), 1)), ), param.params.clone(), param.inferred.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer_types.rs b/src/v1/stage0/src/v1_compiler_infer_types.rs index 04ee1a3e91b..f9dc4f910d1 100644 --- a/src/v1/stage0/src/v1_compiler_infer_types.rs +++ b/src/v1/stage0/src/v1_compiler_infer_types.rs @@ -1487,14 +1487,7 @@ pub fn unify_template( } std::option::Option::None => subst.clone(), }; - match concrete - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match concrete.children.clone().get((1) as usize).cloned() { Some(v) => { if (v1_rt::map_get(&s1, "__value__".to_string()) != std::option::Option::None) @@ -1613,14 +1606,7 @@ pub fn unify_template( ), std::option::Option::None => subst.clone(), }; - match concrete - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match concrete.children.clone().get((1) as usize).cloned() { Some(c) => unify_template( st.clone(), child_type_node(c.clone()), @@ -1656,14 +1642,7 @@ pub fn build_type_substitution( std::option::Option::None => false, }; let non_receiver_templates = if first_is_self.clone() { - Rc::new( - param_templates - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(¶m_templates.clone(), 1)) } else { param_templates.clone() }; @@ -1897,46 +1876,41 @@ pub fn apply_type_substitution( } }, }, - AlgebraTypeTemplate::ReceiverValue => match receiver - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { - Some(child) => Rc::new(KernelTypeBuild { - ty: child_type_node(child.clone()), - diagnostics: Rc::new(vec![]), - }), - std::option::Option::None => { - match v1_rt::map_get(&subst, "__value__".to_string()) { - Some(val) => Rc::new(KernelTypeBuild { - ty: val.clone(), - diagnostics: Rc::new(vec![]), - }), - std::option::Option::None => { - let rname = - container_kind_canonical(crate::v1_std_core::authored_name_at( - source_indices.clone(), - receiver.clone(), - )); - match crate::std_types::container_param_name(rname.clone(), 1) { - Some(n) => Rc::new(KernelTypeBuild { - ty: type_variable_node(n.clone()), - diagnostics: Rc::new(vec![]), - }), - std::option::Option::None => Rc::new(KernelTypeBuild { - ty: missing_kernel_container_profile_type(rname.clone()), - diagnostics: Rc::new(vec![ - kernel_container_profile_miss_diagnostic(rname.clone()), - ]), - }), + AlgebraTypeTemplate::ReceiverValue => { + match receiver.children.clone().get((1) as usize).cloned() { + Some(child) => Rc::new(KernelTypeBuild { + ty: child_type_node(child.clone()), + diagnostics: Rc::new(vec![]), + }), + std::option::Option::None => { + match v1_rt::map_get(&subst, "__value__".to_string()) { + Some(val) => Rc::new(KernelTypeBuild { + ty: val.clone(), + diagnostics: Rc::new(vec![]), + }), + std::option::Option::None => { + let rname = + container_kind_canonical(crate::v1_std_core::authored_name_at( + source_indices.clone(), + receiver.clone(), + )); + match crate::std_types::container_param_name(rname.clone(), 1) { + Some(n) => Rc::new(KernelTypeBuild { + ty: type_variable_node(n.clone()), + diagnostics: Rc::new(vec![]), + }), + std::option::Option::None => Rc::new(KernelTypeBuild { + ty: missing_kernel_container_profile_type(rname.clone()), + diagnostics: Rc::new(vec![ + kernel_container_profile_miss_diagnostic(rname.clone()), + ]), + }), + } } } } } - }, + } AlgebraTypeTemplate::NamedTemplate { name: n, .. } => Rc::new(KernelTypeBuild { ty: nominal_type_ref(n.clone()), diagnostics: Rc::new(vec![]), @@ -2221,13 +2195,7 @@ pub fn node_type_shape_argument_list( match args.clone().first().cloned() { Some(a) => { let head = node_type_shape(child_type_node(a.clone()), source_indices.clone()); - let rest = Rc::new( - args.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&args.clone(), 1)); if ((rest.clone().len() as i64) == 0) { head.clone() } else { @@ -2830,14 +2798,7 @@ pub fn callable_signatures_agree( .iter() .cloned() { - if !(match right - .params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + if !(match right.params.clone().get((pair.0.clone()) as usize).cloned() { Some(right_param) => agree( crate::v1_std_core::param_node_type_expr(pair.1.clone()), crate::v1_std_core::param_node_type_expr(right_param.clone()), @@ -2940,10 +2901,8 @@ pub fn node_type_equals_core( if !(match right .children .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(right_child) => node_type_equals( pair.1.clone(), @@ -3026,18 +2985,14 @@ pub fn node_type_equals_core( Some(right_first) => match left .children .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(left_second) => match right .children .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(right_second) => { (node_type_equals( @@ -3241,14 +3196,7 @@ pub fn method_receiver_element_node( { let normed = normalize_access_type_node(receiver_type.clone()); let maybe_element = if node_is_keyed_collection(normed.clone(), source_indices.clone()) { - match normed - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match normed.children.clone().get((1) as usize).cloned() { Some(ch) => Some(child_type_node(ch.clone())), std::option::Option::None => std::option::Option::None, } @@ -3362,13 +3310,7 @@ pub fn first_matching_algebra_field( } std::option::Option::None => { let __tco_0 = n; - let __tco_1 = Rc::new( - candidates - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_1 = Rc::new(v1_rt::list_skip(&candidates, 1)); let __tco_2 = source_indices; __tco_loop_n = __tco_0; __tco_loop_candidates = __tco_1; diff --git a/src/v1/stage0/src/v1_compiler_ownership.rs b/src/v1/stage0/src/v1_compiler_ownership.rs index 12959c145bd..a69c4f25071 100644 --- a/src/v1/stage0/src/v1_compiler_ownership.rs +++ b/src/v1/stage0/src/v1_compiler_ownership.rs @@ -296,14 +296,10 @@ pub fn merge_branch_usages( let mut __result = Vec::new(); for b in branches.iter().cloned() { __result.extend( - (*Rc::new( - b.fold_call_nodes - .clone() - .iter() - .cloned() - .skip(base_fold_count.clone() as usize) - .collect::>(), - )) + (*Rc::new(v1_rt::list_skip( + &b.fold_call_nodes.clone(), + base_fold_count.clone(), + ))) .iter() .cloned(), ); @@ -1294,7 +1290,7 @@ pub fn analyze_single_fold( Some(a) => crate::v1_std_core::arg_value(a.clone()), std::option::Option::None => method_call.clone(), }; - let fold_lambda_node = match args.clone().iter().cloned().skip(1 as usize).next() { + let fold_lambda_node = match args.clone().get((1) as usize).cloned() { Some(a) => crate::v1_std_core::arg_value(a.clone()), std::option::Option::None => method_call.clone(), }; diff --git a/src/v1/stage0/src/v1_compiler_parse.rs b/src/v1/stage0/src/v1_compiler_parse.rs index 6203bf1f162..1e73153d17c 100644 --- a/src/v1/stage0/src/v1_compiler_parse.rs +++ b/src/v1/stage0/src/v1_compiler_parse.rs @@ -723,8 +723,7 @@ pub fn mint_parsed_string_part_nodes( } }; { - let __tco_0 = - Rc::new(parts.iter().cloned().skip(1 as usize).collect::>()); + let __tco_0 = Rc::new(v1_rt::list_skip(&parts, 1)); let __tco_1 = minted.ctx.clone(); let __tco_2 = span; let __tco_3 = v1_rt::rc_list_push(acc, node.clone()); @@ -3240,14 +3239,7 @@ pub fn stamp_parsed_node_list_with_head_role( } else { { let stamped_tail = stamp_parsed_node_list( - Rc::new( - nodes - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&nodes.clone(), 1)), ancestors.clone(), stamped_head.ctx.clone(), tail_role.clone(), diff --git a/src/v1/stage0/src/v1_compiler_runtime_rust.rs b/src/v1/stage0/src/v1_compiler_runtime_rust.rs index 38d49cab731..c132ac2f070 100644 --- a/src/v1/stage0/src/v1_compiler_runtime_rust.rs +++ b/src/v1/stage0/src/v1_compiler_runtime_rust.rs @@ -76,7 +76,7 @@ pub fn rt_string_ops() -> String { } pub fn rt_collection_ops() -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("pub fn lookup(m: &HashMap, key: K) -> Option {\n".to_string(), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn list_get_optional(items: &Vec, index: i64) -> Option {\n".to_string()), " if index < 0 { None } else { items.get(index as usize).cloned() }\n".to_string()), "}\n\n".to_string()), "pub fn index_by String>(list: Vec, key_fn: F) -> HashMap {\n".to_string()), " let mut map = HashMap::new();\n".to_string()), " for item in list { let key = key_fn(&item); map.insert(key, item); }\n".to_string()), " map\n".to_string()), "}\n\n".to_string()), "pub fn empty_map() -> HashMap { HashMap::new() }\n\n".to_string()), "pub fn map_insert(mut map: HashMap, key: K, value: V) -> HashMap {\n".to_string()), " map.insert(key, value); map\n".to_string()), "}\n\n".to_string()), "pub fn map_merge(mut base: HashMap, overlay: HashMap) -> HashMap {\n".to_string()), " base.extend(overlay); base\n".to_string()), "}\n\n".to_string()), "pub fn map_get(m: &HashMap, key: K) -> Option {\n".to_string()), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn map_keys(m: &HashMap) -> Vec { m.keys().cloned().collect() }\n\n".to_string()), "// THE EMITTED ORDERING-KEY ADMISSION: the counterpart of the interpreter's admit_emitted_ord_keys.\n".to_string()), "// Only String, i64 and bool (and their aliases) are admitted, because on exactly those kinds the\n".to_string()), "// native Ord IS the canonical content order (std.algebra TotalOrder). Any other key type -- a\n".to_string()), "// derived-Ord record or enum (declaration order), or f64 (no total Ord) -- fails to compile here,\n".to_string()), "// at the call, rather than sorting in an order the interpreter would not produce.\n".to_string()), "#[diagnostic::on_unimplemented(message = \"EMIT REFUSED: `{Self}` is not an admitted ordering key; sorted_map_keys and sort_by admit only String, Int and Bool keys, whose order is the canonical content order in both realizations\", label = \"ordering key of a type with no canonical emitted order\")]\n".to_string()), "pub trait CanonicalOrdKey: Ord {}\n".to_string()), "impl CanonicalOrdKey for String {}\n".to_string()), "impl CanonicalOrdKey for RcStr {}\n".to_string()), "impl CanonicalOrdKey for i64 {}\n".to_string()), "impl CanonicalOrdKey for bool {}\n\n".to_string()), "pub fn canonical_key_cmp(a: &K, b: &K) -> std::cmp::Ordering { a.cmp(b) }\n\n".to_string()), "pub fn sorted_map_keys(m: &HashMap) -> Vec { let mut keys = map_keys(m); keys.sort(); keys }\n\n".to_string()), "pub fn map_is_empty(m: &HashMap) -> bool { m.is_empty() }\n\n".to_string()), "// Sound identity hint (see 04_infer.dag substitute_generics): returns true =>\n".to_string()), "// the two are structurally equal. The compiled realization answers with pointer\n".to_string()), "// identity (conservative: may say false when equal); never true-when-unequal, so\n".to_string()), "// callers that return the original on true preserve output structure. Owned Rc\n".to_string()), "// args (cheap refcount bump) keep the inner element pointers, so ptr_eq on the\n".to_string()), "// clones equals ptr_eq on the originals -- lets the .dag pass args by value and\n".to_string()), "// emit through the normal clone path (no all-args-by-ref emit rule needed).\n".to_string()), "pub fn rc_ptr_eq(a: Rc, b: Rc) -> bool { Rc::ptr_eq(&a, &b) }\n\n".to_string()), "pub fn rc_vec_ptr_eq(a: Rc>>, b: Rc>>) -> bool {\n".to_string()), " a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| Rc::ptr_eq(x, y))\n".to_string()), "}\n\n".to_string()), "pub fn map_values(m: &HashMap) -> Vec { m.values().cloned().collect() }\n\n".to_string()), "pub fn list_concat(mut a: Vec, b: Vec) -> Vec { a.append(b); a }\n\n".to_string()), "pub fn list_push(mut list: Vec, item: T) -> Vec { list.push_back(item); list }\n\n".to_string()), "pub fn append(list: Rc>, item: T) -> Vec {\n".to_string()), " let mut v = (*list).clone();\n".to_string()), " v.push_back(item);\n".to_string()), " v\n".to_string()), "}\n\n".to_string()), "pub fn chars_to_string(chars: &Rc>, start: i64, end: i64) -> String {\n".to_string()), " let len = chars.len();\n".to_string()), " let start = (start.max(0) as usize).min(len);\n".to_string()), " let end = (end.max(0) as usize).min(len).max(start);\n".to_string()), " let units = (end.saturating_sub(start)) as u64;\n".to_string()), " record_source_chars_slice_walked(units);\n".to_string()), " // Focus caches the current RRB chunk, so per-char access is contiguous-slice\n".to_string()), " // speed; a plain iter().skip(start) walks O(start) per call, which summed\n".to_string()), " // quadratically across a tokenize pass (profiled 2026-07-06).\n".to_string()), " let mut focus = chars.focus();\n".to_string()), " let mut out = String::with_capacity(end - start);\n".to_string()), " for i in start..end {\n".to_string()), " if let Some(ch) = char::from_u32(*focus.index(i) as u32) {\n".to_string()), " out.push(ch);\n".to_string()), " }\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn parse_int(s: String) -> Option { s.parse::().ok() }\n\n".to_string()), "pub fn map_contains_key(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn map_has(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn rc_empty_set() -> Rc> {\n".to_string()), " Rc::new(BTreeSet::new())\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_insert(mut s: Rc>, x: T) -> Rc> {\n".to_string()), " Rc::make_mut(&mut s).insert(x);\n".to_string()), " s\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_union(a: Rc>, b: Rc>) -> Rc> {\n".to_string()), " let mut out = a;\n".to_string()), " for x in b.iter().cloned() {\n".to_string()), " Rc::make_mut(&mut out).insert(x);\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn set_contains>>(s: S, x: T) -> bool {\n".to_string()), " s.as_ref().contains(&x)\n".to_string()), "}\n\n".to_string()), "pub fn reverse(list: Rc>) -> Rc> {\n".to_string()), " Rc::new(list.iter().cloned().rev().collect())\n".to_string()), "}\n\n".to_string()), "pub fn replace(s: String, from: String, to: String) -> String {\n".to_string()), " s.replace(&from, &to)\n".to_string()), "}\n\n".to_string()) + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("pub fn lookup(m: &HashMap, key: K) -> Option {\n".to_string(), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn list_get_optional(items: &Vec, index: i64) -> Option {\n".to_string()), " if index < 0 { None } else { items.get(index as usize).cloned() }\n".to_string()), "}\n\n".to_string()), "pub fn index_by String>(list: Vec, key_fn: F) -> HashMap {\n".to_string()), " let mut map = HashMap::new();\n".to_string()), " for item in list { let key = key_fn(&item); map.insert(key, item); }\n".to_string()), " map\n".to_string()), "}\n\n".to_string()), "pub fn empty_map() -> HashMap { HashMap::new() }\n\n".to_string()), "pub fn map_insert(mut map: HashMap, key: K, value: V) -> HashMap {\n".to_string()), " map.insert(key, value); map\n".to_string()), "}\n\n".to_string()), "pub fn map_merge(mut base: HashMap, overlay: HashMap) -> HashMap {\n".to_string()), " base.extend(overlay); base\n".to_string()), "}\n\n".to_string()), "pub fn map_get(m: &HashMap, key: K) -> Option {\n".to_string()), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn map_keys(m: &HashMap) -> Vec { m.keys().cloned().collect() }\n\n".to_string()), "// THE EMITTED ORDERING-KEY ADMISSION: the counterpart of the interpreter's admit_emitted_ord_keys.\n".to_string()), "// Only String, i64 and bool (and their aliases) are admitted, because on exactly those kinds the\n".to_string()), "// native Ord IS the canonical content order (std.algebra TotalOrder). Any other key type -- a\n".to_string()), "// derived-Ord record or enum (declaration order), or f64 (no total Ord) -- fails to compile here,\n".to_string()), "// at the call, rather than sorting in an order the interpreter would not produce.\n".to_string()), "#[diagnostic::on_unimplemented(message = \"EMIT REFUSED: `{Self}` is not an admitted ordering key; sorted_map_keys and sort_by admit only String, Int and Bool keys, whose order is the canonical content order in both realizations\", label = \"ordering key of a type with no canonical emitted order\")]\n".to_string()), "pub trait CanonicalOrdKey: Ord {}\n".to_string()), "impl CanonicalOrdKey for String {}\n".to_string()), "impl CanonicalOrdKey for RcStr {}\n".to_string()), "impl CanonicalOrdKey for i64 {}\n".to_string()), "impl CanonicalOrdKey for bool {}\n\n".to_string()), "pub fn canonical_key_cmp(a: &K, b: &K) -> std::cmp::Ordering { a.cmp(b) }\n\n".to_string()), "pub fn sorted_map_keys(m: &HashMap) -> Vec { let mut keys = map_keys(m); keys.sort(); keys }\n\n".to_string()), "pub fn map_is_empty(m: &HashMap) -> bool { m.is_empty() }\n\n".to_string()), "// Sound identity hint (see 04_infer.dag substitute_generics): returns true =>\n".to_string()), "// the two are structurally equal. The compiled realization answers with pointer\n".to_string()), "// identity (conservative: may say false when equal); never true-when-unequal, so\n".to_string()), "// callers that return the original on true preserve output structure. Owned Rc\n".to_string()), "// args (cheap refcount bump) keep the inner element pointers, so ptr_eq on the\n".to_string()), "// clones equals ptr_eq on the originals -- lets the .dag pass args by value and\n".to_string()), "// emit through the normal clone path (no all-args-by-ref emit rule needed).\n".to_string()), "pub fn rc_ptr_eq(a: Rc, b: Rc) -> bool { Rc::ptr_eq(&a, &b) }\n\n".to_string()), "pub fn rc_vec_ptr_eq(a: Rc>>, b: Rc>>) -> bool {\n".to_string()), " a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| Rc::ptr_eq(x, y))\n".to_string()), "}\n\n".to_string()), "pub fn map_values(m: &HashMap) -> Vec { m.values().cloned().collect() }\n\n".to_string()), "pub fn list_concat(mut a: Vec, b: Vec) -> Vec { a.append(b); a }\n\n".to_string()), "pub fn list_push(mut list: Vec, item: T) -> Vec { list.push_back(item); list }\n\n".to_string()), "/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members,\n".to_string()), "/// sharing the receiver's tree in O(log n) instead of copying the remainder, so a walk that\n".to_string()), "/// slices by offset is O(n log n) rather than quadratic. A negative `n` skips every member, as\n".to_string()), "/// the copying form's saturating `n as usize` did.\n".to_string()), "pub fn list_skip(items: &Vec, n: i64) -> Vec {\n".to_string()), " let k = if n < 0 {\n".to_string()), " items.len()\n".to_string()), " } else {\n".to_string()), " (n as usize).min(items.len())\n".to_string()), " };\n".to_string()), " items.skip(k)\n".to_string()), "}\n".to_string()), "\n".to_string()), "/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing\n".to_string()), "/// the receiver's tree. A negative `n` keeps every member, as the copying form did.\n".to_string()), "pub fn list_take(items: &Vec, n: i64) -> Vec {\n".to_string()), " let k = if n < 0 {\n".to_string()), " items.len()\n".to_string()), " } else {\n".to_string()), " (n as usize).min(items.len())\n".to_string()), " };\n".to_string()), " items.take(k)\n".to_string()), "}\n".to_string()), "\n".to_string()), "#[cfg(test)]\n".to_string()), "mod list_slice_tests {\n".to_string()), " use super::*;\n".to_string()), "\n".to_string()), " thread_local! {\n".to_string()), " static CLONES: Cell = const { Cell::new(0) };\n".to_string()), " }\n".to_string()), "\n".to_string()), " /// A code point whose every copy is counted, so a slice's cost is read off the carrier's\n".to_string()), " /// own work rather than a clock.\n".to_string()), " #[derive(Debug, PartialEq)]\n".to_string()), " struct CountedCodePoint(u32);\n".to_string()), "\n".to_string()), " impl Clone for CountedCodePoint {\n".to_string()), " fn clone(&self) -> Self {\n".to_string()), " CLONES.with(|c| c.set(c.get() + 1));\n".to_string()), " CountedCodePoint(self.0)\n".to_string()), " }\n".to_string()), " }\n".to_string()), "\n".to_string()), " fn clones_during(f: impl FnOnce() -> R) -> (R, u64) {\n".to_string()), " let before = CLONES.with(|c| c.get());\n".to_string()), " let r = f();\n".to_string()), " (r, CLONES.with(|c| c.get()) - before)\n".to_string()), " }\n".to_string()), "\n".to_string()), " // Non-ASCII on purpose: emitted char_at / substring over a bare &str are O(offset) here,\n".to_string()), " // which is the walk the code-point slice replaces.\n".to_string()), " fn non_ascii_code_points(n: usize) -> Vec {\n".to_string()), " [0xe9u32, 0x4e2d, 0x1f600, 0x61]\n".to_string()), " .iter()\n".to_string()), " .cycle()\n".to_string()), " .take(n)\n".to_string()), " .map(|cp| CountedCodePoint(*cp))\n".to_string()), " .collect()\n".to_string()), " }\n".to_string()), "\n".to_string()), " const N: usize = 200_000;\n".to_string()), " // im's RRB split copies at most one boundary chunk per tree level; this is that bound with\n".to_string()), " // headroom, and three orders of magnitude below N.\n".to_string()), " const SLICE_COPY_BUDGET: u64 = 2_048;\n".to_string()), "\n".to_string()), " #[test]\n".to_string()), " fn a_slice_from_the_middle_copies_only_boundary_chunks() {\n".to_string()), " let cps = Rc::new(non_ascii_code_points(N));\n".to_string()), " let (slice, copied) = clones_during(|| list_take(&list_skip(&cps, (N / 2) as i64), 7));\n".to_string()), " assert!(\n".to_string()), " copied <= SLICE_COPY_BUDGET,\n".to_string()), " \"skip+take copied {} members of {}\",\n".to_string()), " copied,\n".to_string()), " N\n".to_string()), " );\n".to_string()), " let expected: std::vec::Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect();\n".to_string()), " assert_eq!(\n".to_string()), " slice.iter().map(|c| c.0).collect::>(),\n".to_string()), " expected\n".to_string()), " );\n".to_string()), " }\n".to_string()), "\n".to_string()), " // THE RED CONTROL: the extdeps.languages.rust.emit skip / take templates this runtime pair\n".to_string()), " // replaced, spelled as they emitted, on the same input and bound. It must exceed the budget,\n".to_string()), " // or the budget does not discriminate the quadratic.\n".to_string()), " #[test]\n".to_string()), " fn the_copying_template_form_exceeds_the_budget() {\n".to_string()), " let cps = Rc::new(non_ascii_code_points(N));\n".to_string()), " let (_, copied) = clones_during(|| {\n".to_string()), " let rest = cps.iter().cloned().skip(N / 2).collect::>();\n".to_string()), " rest.iter().cloned().take(7).collect::>()\n".to_string()), " });\n".to_string()), " assert!(\n".to_string()), " copied > SLICE_COPY_BUDGET,\n".to_string()), " \"copying form copied only {}\",\n".to_string()), " copied\n".to_string()), " );\n".to_string()), " }\n".to_string()), "\n".to_string()), " #[test]\n".to_string()), " fn negative_and_overlong_counts_keep_the_copying_forms_reading() {\n".to_string()), " let xs: Vec = (0..10).collect();\n".to_string()), " for n in [-3i64, 0, 4, 10, 11, i64::MAX] {\n".to_string()), " let copying_skip = xs.iter().cloned().skip(n as usize).collect::>();\n".to_string()), " let copying_take = xs.iter().cloned().take(n as usize).collect::>();\n".to_string()), " assert_eq!(list_skip(&xs, n), copying_skip, \"skip {}\", n);\n".to_string()), " assert_eq!(list_take(&xs, n), copying_take, \"take {}\", n);\n".to_string()), " }\n".to_string()), " }\n".to_string()), "}\n".to_string()), "\n".to_string()), "pub fn append(list: Rc>, item: T) -> Vec {\n".to_string()), " let mut v = (*list).clone();\n".to_string()), " v.push_back(item);\n".to_string()), " v\n".to_string()), "}\n\n".to_string()), "pub fn chars_to_string(chars: &Rc>, start: i64, end: i64) -> String {\n".to_string()), " let len = chars.len();\n".to_string()), " let start = (start.max(0) as usize).min(len);\n".to_string()), " let end = (end.max(0) as usize).min(len).max(start);\n".to_string()), " let units = (end.saturating_sub(start)) as u64;\n".to_string()), " record_source_chars_slice_walked(units);\n".to_string()), " // Focus caches the current RRB chunk, so per-char access is contiguous-slice\n".to_string()), " // speed; a plain iter().skip(start) walks O(start) per call, which summed\n".to_string()), " // quadratically across a tokenize pass (profiled 2026-07-06).\n".to_string()), " let mut focus = chars.focus();\n".to_string()), " let mut out = String::with_capacity(end - start);\n".to_string()), " for i in start..end {\n".to_string()), " if let Some(ch) = char::from_u32(*focus.index(i) as u32) {\n".to_string()), " out.push(ch);\n".to_string()), " }\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn parse_int(s: String) -> Option { s.parse::().ok() }\n\n".to_string()), "pub fn map_contains_key(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn map_has(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn rc_empty_set() -> Rc> {\n".to_string()), " Rc::new(BTreeSet::new())\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_insert(mut s: Rc>, x: T) -> Rc> {\n".to_string()), " Rc::make_mut(&mut s).insert(x);\n".to_string()), " s\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_union(a: Rc>, b: Rc>) -> Rc> {\n".to_string()), " let mut out = a;\n".to_string()), " for x in b.iter().cloned() {\n".to_string()), " Rc::make_mut(&mut out).insert(x);\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn set_contains>>(s: S, x: T) -> bool {\n".to_string()), " s.as_ref().contains(&x)\n".to_string()), "}\n\n".to_string()), "pub fn reverse(list: Rc>) -> Rc> {\n".to_string()), " Rc::new(list.iter().cloned().rev().collect())\n".to_string()), "}\n\n".to_string()), "pub fn replace(s: String, from: String, to: String) -> String {\n".to_string()), " s.replace(&from, &to)\n".to_string()), "}\n\n".to_string()) } pub fn rt_string_carrier() -> String { diff --git a/src/v1/stage0/src/v1_compiler_trace.rs b/src/v1/stage0/src/v1_compiler_trace.rs index 1637cff79ed..a86f63edfb6 100644 --- a/src/v1/stage0/src/v1_compiler_trace.rs +++ b/src/v1/stage0/src/v1_compiler_trace.rs @@ -98,15 +98,10 @@ pub fn trace_pop_frame(trace: Rc) -> Rc { } else { Rc::new(Trace { events: trace.events.clone(), - stack: Rc::new( - trace - .stack - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub(n.clone(), 1) as usize) - .collect::>(), - ), + stack: Rc::new(v1_rt::list_take( + &trace.stack.clone(), + v1_rt::int_sub(n.clone(), 1), + )), }) } } diff --git a/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs b/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs index 4f1778fa883..e022f34832a 100644 --- a/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs +++ b/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs @@ -2106,22 +2106,8 @@ pub fn v1_ord_propagated_zip_loop( (here.clone() || v1_ord_propagated_zip_loop( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), decl_set_affected_names.clone(), source_indices.clone(), )) @@ -2775,22 +2761,8 @@ pub fn v1_declared_type_app_mentions_param_non_phantom_loop( (here.clone() || v1_declared_type_app_mentions_param_non_phantom_loop( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), phantom_slot_names.clone(), bounds.clone(), type_decl_items.clone(), @@ -3652,22 +3624,8 @@ pub fn v1_declared_type_app_clone_impl_needs_param_loop( (here.clone() || v1_declared_type_app_clone_impl_needs_param_loop( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), phantom_slot_names.clone(), bounds.clone(), type_decl_items.clone(), @@ -3735,22 +3693,8 @@ pub fn v1_declared_arg_positions_need_clone_param( (here.clone() || v1_declared_arg_positions_need_clone_param( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), bound_params.clone(), bounds.clone(), type_decl_items.clone(), diff --git a/src/v1/stage0/src/v1_interpreter.rs b/src/v1/stage0/src/v1_interpreter.rs index 6ed58544e37..d06324cbc18 100644 --- a/src/v1/stage0/src/v1_interpreter.rs +++ b/src/v1/stage0/src/v1_interpreter.rs @@ -1100,16 +1100,74 @@ fn value_eq_calls() -> u64 { /// onto a heap worklist and dropped from there with its own children already detached. Shared /// children (`Rc` strong count above one) are left in place; their last owner detaches them. /// Class: gunbc.recurring_failure_mode recursion_over_value_depth_uncounted_by_the_call_limit. +/// +/// A PERSISTENT CARRIER IS DETACHED ONLY ONCE DROP NESTING IS DEEP. A List or Map is an RRB / HAMT +/// tree whose nodes may be shared with other values -- a `skip` or `map_insert` result shares +/// all but a logarithmic boundary with its source -- and consuming one into the worklist clones +/// every member out of the shared nodes, so dropping a slice cost its source's length and a walk +/// that slices by offset was quadratic in the drop alone. Detaching exists for host-stack depth, +/// and depth only accrues where one carrier's drop nests another's: below +/// `VALUE_DROP_INLINE_DEPTH` nested drops the carrier is released by its own drop, which frees +/// shared nodes by reference count and runs each member's `Value::drop` (itself iterative over +/// Variant / Record depth); at or beyond it the carrier is detached as before, so a list nested +/// to any depth still drops in bounded host stack (`value_depth_walker_tests::a_deep_list_drops`). impl Drop for Value { fn drop(&mut self) { + let depth = VALUE_DROP_DEPTH + .try_with(|d| { + let depth = d.get(); + d.set(depth + 1); + depth + }) + .unwrap_or(VALUE_DROP_INLINE_DEPTH); + let detach_carriers = depth >= VALUE_DROP_INLINE_DEPTH; let mut pending: Vec = Vec::new(); - detach_owned_children(self, &mut pending); + detach_owned_children(self, &mut pending, detach_carriers); while let Some(mut child) = pending.pop() { - detach_owned_children(&mut child, &mut pending); + detach_owned_children(&mut child, &mut pending, detach_carriers); + } + // A carrier left attached is released HERE, while the depth is raised: drop glue would + // release it only after this function returns and the depth is restored, and a nested + // carrier's members would then never see the depth they are at. + match self { + Value::List(items) => drop(std::mem::replace(items, empty_list_carrier())), + Value::Map(entries) => drop(std::mem::replace(entries, empty_map_carrier())), + _ => {} } + let _ = VALUE_DROP_DEPTH.try_with(|d| d.set(depth)); } } +thread_local! { + static EMPTY_LIST_CARRIER: Rc> = Rc::new(RrbVector::new()); + static EMPTY_MAP_CARRIER: Rc> = Rc::new(HamtMap::new()); +} + +fn empty_list_carrier() -> Rc> { + EMPTY_LIST_CARRIER + .try_with(Rc::clone) + .unwrap_or_else(|_| Rc::new(RrbVector::new())) +} + +fn empty_map_carrier() -> Rc> { + EMPTY_MAP_CARRIER + .try_with(Rc::clone) + .unwrap_or_else(|_| Rc::new(HamtMap::new())) +} + +const VALUE_DROP_INLINE_DEPTH: u32 = 64; + +thread_local! { + static VALUE_DROP_DEPTH: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +// TEST-ONLY WORK OBSERVATION: members moved out of a persistent carrier by drop, so the drop's +// cost contract is observed as work rather than time. +#[cfg(test)] +thread_local! { + static DROP_DETACHED_CARRIER_MEMBERS: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + /// The portable form is a plain tree with no sharing, so it is as deep as the value it was taken /// from and needs the same iterative drop (`impl Drop for Value`): a recursive drop of a deep /// portable chain aborted the process (stack overflow) in @@ -1141,15 +1199,19 @@ fn detach_portable_children(value: &mut PortableValue, pending: &mut Vec) { +fn detach_owned_children(value: &mut Value, pending: &mut Vec, detach_carriers: bool) { match value { - Value::List(items) => { + Value::List(items) if detach_carriers => { if let Some(items) = Rc::get_mut(items) { + #[cfg(test)] + DROP_DETACHED_CARRIER_MEMBERS.with(|c| c.set(c.get() + items.len() as u64)); pending.extend(std::mem::take(items)); } } - Value::Map(entries) => { + Value::Map(entries) if detach_carriers => { if let Some(entries) = Rc::get_mut(entries) { + #[cfg(test)] + DROP_DETACHED_CARRIER_MEMBERS.with(|c| c.set(c.get() + entries.len() as u64)); pending.extend(std::mem::take(entries).into_iter().map(|(_, v)| v)); } } @@ -14229,17 +14291,13 @@ macro_rules! v1_algebra_method_arms { arm "method_call.skip" { "skip" } => { let items = expect_list(&$receiver, "skip")?; let n = expect_int($args.first(), "skip")?; - Ok(list_value( - items.iter().skip(n as usize).cloned().collect::>(), - )) + Ok(Value::List(Rc::new(rrb_skip(&items, n)))) }, arm "method_call.take" { "take" } => { let items = expect_list(&$receiver, "take")?; let n = expect_int($args.first(), "take")?; - Ok(list_value( - items.iter().take(n as usize).cloned().collect::>(), - )) + Ok(Value::List(Rc::new(rrb_take(&items, n)))) }, arm "method_call.enumerate" { "enumerate" } => { @@ -23604,7 +23662,7 @@ macro_rules! v1_builtin_arms { arm "free_call.chars_to_string" { "chars_to_string" } => { let cps = match $positional.first().copied() { - Some(v) => free_monoid_to_vec(v).ok_or_else(|| InterpError::TypeError { + Some(v) => free_monoid_items(v).ok_or_else(|| InterpError::TypeError { msg: "chars_to_string expects a list of code points".to_string(), })?, None => { @@ -23621,7 +23679,9 @@ macro_rules! v1_builtin_arms { .max(0) .min(len) .max(start); - let s: String = cps[start as usize..end as usize] + let s: String = cps + .skip(start as usize) + .take((end - start) as usize) .iter() .filter_map(|v| match v { Value::Int(cp) => char::from_u32(*cp as u32), @@ -23632,6 +23692,10 @@ macro_rules! v1_builtin_arms { }, arm "free_call.get" { "get" } => match $positional.as_slice() { + [Value::List(items), idx_val] => { + let idx = expect_int(Some(idx_val), "get")?; + Ok(Some(list_get_at_or_null(items, idx))) + } [list_val, idx_val] if free_monoid_to_vec(list_val).is_some() => { let items = expect_list(list_val, "get")?; let idx = expect_int(Some(idx_val), "get")?; @@ -23712,6 +23776,7 @@ macro_rules! v1_builtin_arms { }, arm "free_call.count" { "count" } => match $positional.first() { + Some(Value::List(items)) => Ok(Some(Value::Int(items.len() as i64))), Some(v) => match free_monoid_to_vec(v) { Some(items) => Ok(Some(Value::Int(items.len() as i64))), None => Ok(None), @@ -26486,6 +26551,183 @@ fn value_to_list_carrier(val: &Value) -> Option<(Rc>, u64)> { } } +/// The members of a code-point (or any FreeMonoid) value as the persistent carrier, sharing a +/// `Value::List`'s tree rather than flattening it. `free_monoid_to_vec` copies all n members, so a +/// consumer that reads a slice through it pays O(n) per read and O(n^2) over a walk; this reads the +/// list form in O(1) and flattens only the Str and Cons forms, which have no shared tree to borrow. +fn free_monoid_items(val: &Value) -> Option>> { + match val { + Value::List(items) => Some(Rc::clone(items)), + other => free_monoid_to_vec(other).map(|items| Rc::new(RrbVector::from(items))), + } +} + +/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members, sharing +/// the receiver's tree, O(log n) rather than a copy of the remainder. A negative `n` keeps the +/// reading the copying form had (`n as usize` saturates past the end), which skips everything. +pub(crate) fn rrb_skip(items: &RrbVector, n: i64) -> RrbVector { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.skip(k) +} + +/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing the +/// receiver's tree. A negative `n` keeps every member, as the copying form's saturating cast did. +pub(crate) fn rrb_take(items: &RrbVector, n: i64) -> RrbVector { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.take(k) +} + +#[cfg(test)] +mod slice_cost_tests { + use super::*; + use std::cell::Cell; + + thread_local! { + static CLONES: Cell = const { Cell::new(0) }; + } + + /// A code point whose every copy is counted, so a slice's cost is read off the carrier's + /// own work rather than a clock. + #[derive(Debug, PartialEq)] + struct CountedCodePoint(u32); + + impl Clone for CountedCodePoint { + fn clone(&self) -> Self { + CLONES.with(|c| c.set(c.get() + 1)); + CountedCodePoint(self.0) + } + } + + fn clones_during(f: impl FnOnce() -> R) -> (R, u64) { + let before = CLONES.with(|c| c.get()); + let r = f(); + (r, CLONES.with(|c| c.get()) - before) + } + + // Non-ASCII on purpose: the interpreter's string carrier is O(1) per index only on ASCII, + // which is the case a byte-offset decode is NOT linear on. + fn non_ascii_code_points(n: usize) -> RrbVector { + "\u{e9}\u{4e2d}\u{1f600}a" + .chars() + .cycle() + .take(n) + .map(|c| CountedCodePoint(c as u32)) + .collect() + } + + const N: usize = 200_000; + // im's RRB split copies at most one boundary chunk per tree level; this is that bound with + // headroom, and three orders of magnitude below N. + const SLICE_COPY_BUDGET: u64 = 2_048; + + #[test] + fn a_slice_from_the_middle_copies_only_boundary_chunks() { + let cps = non_ascii_code_points(N); + let (slice, copied) = clones_during(|| rrb_take(&rrb_skip(&cps, (N / 2) as i64), 7)); + assert!( + copied <= SLICE_COPY_BUDGET, + "skip+take copied {copied} members of {N}" + ); + let expected: Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect(); + assert_eq!(slice.iter().map(|c| c.0).collect::>(), expected); + } + + // THE RED CONTROL: the copying form these arms had, on the same input and bound. It must + // exceed the budget, or the budget does not discriminate the quadratic. + #[test] + fn the_copying_slice_form_exceeds_the_budget() { + let cps = non_ascii_code_points(N); + let (_, copied) = clones_during(|| { + let rest: RrbVector<_> = cps.iter().skip(N / 2).cloned().collect(); + rest.iter().take(7).cloned().collect::>() + }); + assert!( + copied > SLICE_COPY_BUDGET, + "copying form copied only {copied}" + ); + } + + #[test] + fn negative_and_overlong_counts_keep_the_copying_forms_reading() { + let cps: RrbVector = (0..10).collect(); + let copying_skip = |n: i64| { + cps.iter() + .skip(n as usize) + .cloned() + .collect::>() + }; + let copying_take = |n: i64| { + cps.iter() + .take(n as usize) + .cloned() + .collect::>() + }; + for n in [-3, 0, 4, 10, 11, i64::MAX] { + assert_eq!(rrb_skip(&cps, n), copying_skip(n), "skip {n}"); + assert_eq!(rrb_take(&cps, n), copying_take(n), "take {n}"); + } + } + + fn detached() -> u64 { + DROP_DETACHED_CARRIER_MEMBERS.with(|c| c.get()) + } + + // The slice's drop is part of the slice's cost: a walk drops one slice per step. Below the + // nesting threshold the RRB tree releases its nodes by reference count, so dropping a slice + // of a shared list moves no member; the copying drop moved the whole suffix. + #[test] + fn dropping_a_slice_of_a_shared_list_moves_no_member() { + let base = list_value((0..N as i64).map(Value::Int).collect::>()); + let Value::List(items) = &base else { + unreachable!("list_value builds a list") + }; + let slice = Value::List(Rc::new(rrb_skip(items, (N / 2) as i64))); + let before = detached(); + drop(slice); + assert_eq!(detached() - before, 0, "dropping a shared slice walked it"); + assert_eq!(items.len(), N, "the source survives its slice's drop"); + } + + // THE RED CONTROL: the same drop once nesting is past the threshold, which is the detaching + // path every list drop used to take. It moves the slice's members, so the counter discriminates. + #[test] + fn the_detaching_drop_moves_the_slice_members() { + let base = list_value((0..N as i64).map(Value::Int).collect::>()); + let Value::List(items) = &base else { + unreachable!("list_value builds a list") + }; + let mut slice = Value::List(Rc::new(rrb_skip(items, (N / 2) as i64))); + let before = detached(); + let mut pending = Vec::new(); + detach_owned_children(&mut slice, &mut pending, true); + assert_eq!(detached() - before, (N - N / 2) as u64); + } + + #[test] + fn reading_a_list_value_does_not_flatten_it() { + let list = list_value((0..N as i64).map(Value::Int).collect::>()); + let before = flatten_counters_snapshot(); + let items = free_monoid_items(&list).expect("a list is a free monoid"); + assert_eq!(items.len(), N); + assert_eq!( + flatten_counters_snapshot(), + before, + "free_monoid_items flattened a Value::List" + ); + // RED CONTROL: the flattening read the get / count / chars_to_string arms used. + free_monoid_to_vec(&list).expect("a list is a free monoid"); + assert_eq!(flatten_counters_snapshot().1 - before.1, N as u64); + } +} + fn list_get_at_or_null(items: &RrbVector, idx: i64) -> Value { items.get(idx as usize).cloned().unwrap_or(Value::Null) } diff --git a/src/v1/stage0/src/v1_rt.rs b/src/v1/stage0/src/v1_rt.rs index 7204701cdf4..f4746f4f95b 100644 --- a/src/v1/stage0/src/v1_rt.rs +++ b/src/v1/stage0/src/v1_rt.rs @@ -758,6 +758,118 @@ pub fn list_push(mut list: Vec, item: T) -> Vec { list } +/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members, +/// sharing the receiver's tree in O(log n) instead of copying the remainder, so a walk that +/// slices by offset is O(n log n) rather than quadratic. A negative `n` skips every member, as +/// the copying form's saturating `n as usize` did. +pub fn list_skip(items: &Vec, n: i64) -> Vec { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.skip(k) +} + +/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing +/// the receiver's tree. A negative `n` keeps every member, as the copying form did. +pub fn list_take(items: &Vec, n: i64) -> Vec { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.take(k) +} + +#[cfg(test)] +mod list_slice_tests { + use super::*; + + thread_local! { + static CLONES: Cell = const { Cell::new(0) }; + } + + /// A code point whose every copy is counted, so a slice's cost is read off the carrier's + /// own work rather than a clock. + #[derive(Debug, PartialEq)] + struct CountedCodePoint(u32); + + impl Clone for CountedCodePoint { + fn clone(&self) -> Self { + CLONES.with(|c| c.set(c.get() + 1)); + CountedCodePoint(self.0) + } + } + + fn clones_during(f: impl FnOnce() -> R) -> (R, u64) { + let before = CLONES.with(|c| c.get()); + let r = f(); + (r, CLONES.with(|c| c.get()) - before) + } + + // Non-ASCII on purpose: emitted char_at / substring over a bare &str are O(offset) here, + // which is the walk the code-point slice replaces. + fn non_ascii_code_points(n: usize) -> Vec { + [0xe9u32, 0x4e2d, 0x1f600, 0x61] + .iter() + .cycle() + .take(n) + .map(|cp| CountedCodePoint(*cp)) + .collect() + } + + const N: usize = 200_000; + // im's RRB split copies at most one boundary chunk per tree level; this is that bound with + // headroom, and three orders of magnitude below N. + const SLICE_COPY_BUDGET: u64 = 2_048; + + #[test] + fn a_slice_from_the_middle_copies_only_boundary_chunks() { + let cps = Rc::new(non_ascii_code_points(N)); + let (slice, copied) = clones_during(|| list_take(&list_skip(&cps, (N / 2) as i64), 7)); + assert!( + copied <= SLICE_COPY_BUDGET, + "skip+take copied {} members of {}", + copied, + N + ); + let expected: std::vec::Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect(); + assert_eq!( + slice.iter().map(|c| c.0).collect::>(), + expected + ); + } + + // THE RED CONTROL: the extdeps.languages.rust.emit skip / take templates this runtime pair + // replaced, spelled as they emitted, on the same input and bound. It must exceed the budget, + // or the budget does not discriminate the quadratic. + #[test] + fn the_copying_template_form_exceeds_the_budget() { + let cps = Rc::new(non_ascii_code_points(N)); + let (_, copied) = clones_during(|| { + let rest = cps.iter().cloned().skip(N / 2).collect::>(); + rest.iter().cloned().take(7).collect::>() + }); + assert!( + copied > SLICE_COPY_BUDGET, + "copying form copied only {}", + copied + ); + } + + #[test] + fn negative_and_overlong_counts_keep_the_copying_forms_reading() { + let xs: Vec = (0..10).collect(); + for n in [-3i64, 0, 4, 10, 11, i64::MAX] { + let copying_skip = xs.iter().cloned().skip(n as usize).collect::>(); + let copying_take = xs.iter().cloned().take(n as usize).collect::>(); + assert_eq!(list_skip(&xs, n), copying_skip, "skip {}", n); + assert_eq!(list_take(&xs, n), copying_take, "take {}", n); + } + } +} + pub fn append(list: Rc>, item: T) -> Vec { let mut v = (*list).clone(); v.push_back(item); diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index cabb658e176..6e921d69cbc 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -2221,7 +2221,7 @@ pub fn param_node_type_expr(n: Rc) -> Rc { pub fn param_node_default_value(n: Rc) -> Option> { if ((n.children.clone().len() as i64) > 1) { - n.children.clone().iter().cloned().skip(1 as usize).next() + n.children.clone().get((1) as usize).cloned() } else { std::option::Option::None } @@ -2298,7 +2298,7 @@ pub fn field_node_cardinality(n: Rc) -> Cardinality { pub fn field_node_default_value(n: Rc) -> Option> { if ((n.children.clone().len() as i64) > 1) { - n.children.clone().iter().cloned().skip(1 as usize).next() + n.children.clone().get((1) as usize).cloned() } else { std::option::Option::None } @@ -2570,10 +2570,8 @@ pub fn expr_child_at(texpr: Rc, index: i64, role: String) -> Rc { match texpr .children .clone() - .iter() + .get((index.clone()) as usize) .cloned() - .skip(index.clone() as usize) - .next() { Some(v) => v.clone(), std::option::Option::None => make_expr_error_node( @@ -2831,13 +2829,7 @@ pub fn if_then_branch(texpr: Rc) -> Rc { } pub fn if_else_branch(texpr: Rc) -> Option> { - texpr - .children - .clone() - .iter() - .cloned() - .skip(2 as usize) - .next() + texpr.children.clone().get((2) as usize).cloned() } pub fn match_scrutinee(texpr: Rc) -> Rc { @@ -2845,15 +2837,7 @@ pub fn match_scrutinee(texpr: Rc) -> Rc { } pub fn match_arm_nodes(texpr: Rc) -> Rc>> { - Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)) } pub fn binop_left(texpr: Rc) -> Rc { @@ -2953,15 +2937,7 @@ pub fn method_receiver(texpr: Rc) -> Rc { } pub fn method_arg_nodes(texpr: Rc) -> Rc>> { - Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)) } pub fn expr_method_name_at( @@ -2991,17 +2967,9 @@ pub fn lambda_param_names_at( ) -> Rc> { Rc::new({ let mut __result = Vec::new(); - for n in Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() + for n in Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)) + .iter() + .cloned() { __result.push(authored_name_at(source_indices.clone(), n.clone())); } @@ -3014,13 +2982,7 @@ pub fn let_value(texpr: Rc) -> Rc { } pub fn let_body(texpr: Rc) -> Option> { - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() + texpr.children.clone().get((1) as usize).cloned() } pub fn let_binding_name_at( @@ -5107,10 +5069,8 @@ pub fn byte_to_line_col(index: Rc, offset: i64) -> LineCol { match index .offsets .clone() - .iter() + .get((v1_rt::int_sub(line.clone(), 2)) as usize) .cloned() - .skip(v1_rt::int_sub(line.clone(), 2) as usize) - .next() { Some(o) => v1_rt::int_add(o.clone(), 1), std::option::Option::None => 0, @@ -5133,10 +5093,8 @@ pub fn source_line_at(index: Rc, line: i64) -> String { match index .offsets .clone() - .iter() + .get((v1_rt::int_sub(line.clone(), 2)) as usize) .cloned() - .skip(v1_rt::int_sub(line.clone(), 2) as usize) - .next() { Some(o) => v1_rt::int_add(o.clone(), 1), std::option::Option::None => src_len.clone(), @@ -5145,10 +5103,8 @@ pub fn source_line_at(index: Rc, line: i64) -> String { let line_end = match index .offsets .clone() - .iter() + .get((v1_rt::int_sub(line.clone(), 1)) as usize) .cloned() - .skip(v1_rt::int_sub(line.clone(), 1) as usize) - .next() { Some(o) => o.clone(), std::option::Option::None => src_len.clone(), diff --git a/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs b/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs index 5ad0c3b241f..bc8bf680a15 100644 --- a/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs +++ b/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs @@ -1576,15 +1576,7 @@ pub fn gi_owner_argument_arms_discriminate( xs_param.clone(), v1_rt::concat( Rc::new(vec![minted.clone()]), - Rc::new( - xs_param - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&xs_param.children.clone(), 1)), ), ); (((gi_foreign_label_under_owner( diff --git a/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs b/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs index 642898b05bf..9de31d86589 100644 --- a/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs +++ b/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs @@ -418,16 +418,9 @@ pub fn ihc_template_holes(ctx: Rc, template: Rc) -> Rc>(), - )) - .iter() - .cloned(), + (*Rc::new(v1_rt::list_take(&p.children.clone(), 1))) + .iter() + .cloned(), ); } __result