diff --git a/dag/gunbc/recurring_failure_mode/ownership_branch_join_discards_whole_value_read.dag b/dag/gunbc/recurring_failure_mode/ownership_branch_join_discards_whole_value_read.dag new file mode 100644 index 00000000000..01880f1d2ff --- /dev/null +++ b/dag/gunbc/recurring_failure_mode/ownership_branch_join_discards_whole_value_read.dag @@ -0,0 +1,18 @@ +module gunbc.recurring_failure_mode.ownership_branch_join_discards_whole_value_read + +import std.types { NonEmptyStr } +import gunbc.recurring_failure_mode { RecurringFailureMode } + +// Consumer: gunbc.design_ledgers renders this class through directory-derived row membership. +data ownership_branch_join_discards_whole_value_read: RecurringFailureMode = RecurringFailureMode { + identity: "ownership_branch_join_discards_whole_value_read" as NonEmptyStr, + receipts: [ + "INVALID STATE: ownership.max_usage_by_fan_out selects a branch usage list with more projections and discards another branch's whole-value Read. build_movable_set then admits a move while another use still needs the value.", + "HARM AND FOUND RUNG: emit_var_ref emits a move into a constructor before a subsequent projection; rustc refuses E0382. The defect is loudly mitigated by the target borrow check, not silently accepted.", + "REPAIR: max_usage_by_fan_out retains missing Read evidence from either binding usage, deduplicating shared-prefix reads and keeping Projected distinct. The emitter carries no new rule. Corpus-grain prevention is mechanical through the required regen-fixed-point phase, which re-derives the committed stage0 mirrors and refuses divergence.", + "FIXTURE EVIDENCE: ownership_branch_read Rust integration tests call the emitted ownership mirror with the same three BindingUsage fixtures and compile_sources on the authored move probe. cargo test -p v1-compiler --test ownership_branch_read executes them directly. Required clippy --all-targets compiles them, but no CI step runs them; gunbc.rung_drop.rust_unit_tests_off_the_merge_path declares that execution drop.", + "NEXT-RUNG HARNESS TRIGGER: a required phase in an existing lane that executes seed-rooted claims and writes same-identity, candidate-bound terminals into the disposition receipt. The fixture controls return to .dag when that capability lands. The missing harness is the next-rung trigger, not a permanent ceiling. Work item node://adhoc-babdd2de-8db, owned by eager-raven-113, carries this model-first harness in an existing lane and dissolves with v1 deletion. Witness roots remain dag and src/v2; adding the compiler root for one fixture would widen the floor's subject.", + "COST RESIDUE: Read is non-tail residue in the existing ownership model, so both constructor orders conservatively clone. This is bounded cost, not a correctness gap. Its separate next-rung capability is per-path last-use evidence at the ownership fold.", + ], + evidence: [], +} diff --git a/src/v1/ownership.dag b/src/v1/ownership.dag index 8dec0b7ad43..a446a2a76c7 100644 --- a/src/v1/ownership.dag +++ b/src/v1/ownership.dag @@ -124,8 +124,23 @@ fn record_use(accum: UsageAccum, name: String, kind: EdgeKind, site: String, bin } } +// Consumer: build_movable_set must see whole-value Read evidence from every branch. +// Fan-out still selects the path for semantic consumer counts; reads on another path must not +// disappear merely because that path has fewer projections. Shared-prefix reads are retained +// once, so repeated joins do not manufacture extra uses. Projected remains distinct from Read. +// Read is non-tail residue: retaining it conservatively clones until the ownership fold carries +// per-path last-use evidence. No emitter ordering or move rule is introduced here. fn max_usage_by_fan_out(a: BindingUsage, b: BindingUsage) -> BindingUsage { - if binding_fan_out(usage: b) > binding_fan_out(usage: a) { b } else { a } + let b_is_larger = binding_fan_out(usage: b) > binding_fan_out(usage: a) + let selected = if b_is_larger { b } else { a } + let other = if b_is_larger { a } else { b } + let missing_reads = other.consumers |> filter(edge => + match edge.kind { + Read => selected.consumers |> any(existing => existing.kind == Read && existing.span_start == edge.span_start && existing.site == edge.site) == false + _ => false + } + ) + BindingUsage { name: selected.name, binding_kind: selected.binding_kind, consumers: concat(selected.consumers, missing_reads) } } fn map_usage_merge_at(base: Map, key: String, new_val: BindingUsage) -> Map { diff --git a/src/v1/stage0/src/extdeps_container_oci_digest.rs b/src/v1/stage0/src/extdeps_container_oci_digest.rs index a8ac4a28e78..3dfd4157932 100644 --- a/src/v1/stage0/src/extdeps_container_oci_digest.rs +++ b/src/v1/stage0/src/extdeps_container_oci_digest.rs @@ -106,10 +106,10 @@ pub fn oci_digest_algorithm_syntax_valid( ) -> bool { loop { if (index.clone() >= v1_rt::string_length(&text)) { - break in_component; + break in_component.clone(); } else { let cp = v1_rt::code_point(v1_rt::substring(&text, index.clone(), (index.clone() + 1))); - if in_component { + if in_component.clone() { if oci_digest_algorithm_component_char_allowed(cp.clone()) { { let __tco_0 = (index + 1); diff --git a/src/v1/stage0/src/extdeps_version_semver.rs b/src/v1/stage0/src/extdeps_version_semver.rs index f20c72fe7bf..0fa69e49fac 100644 --- a/src/v1/stage0/src/extdeps_version_semver.rs +++ b/src/v1/stage0/src/extdeps_version_semver.rs @@ -193,10 +193,10 @@ pub fn semver_version_label(v: Rc) -> String { ) }; if ((v.build.clone().len() as i64) == 0) { - with_pre + with_pre.clone() } else { v1_rt::concat( - with_pre, + with_pre.clone(), v1_rt::concat("+".to_string(), semver_identifiers_label(v.build.clone())), ) } diff --git a/src/v1/stage0/src/std_dissolution.rs b/src/v1/stage0/src/std_dissolution.rs index 5bf26e4f0ec..ff77b6ed920 100644 --- a/src/v1/stage0/src/std_dissolution.rs +++ b/src/v1/stage0/src/std_dissolution.rs @@ -69,8 +69,8 @@ pub fn dissolution_trigger_fired( present_decls.clone(), ); match (*trigger.clone()).clone() { - DissolutionTrigger::DeclarationAppears { ref_: _, .. } => present, - DissolutionTrigger::DeclarationRetires { ref_: _, .. } => !present, + DissolutionTrigger::DeclarationAppears { ref_: _, .. } => present.clone(), + DissolutionTrigger::DeclarationRetires { ref_: _, .. } => !present.clone(), } } } diff --git a/src/v1/stage0/src/std_induction.rs b/src/v1/stage0/src/std_induction.rs index de053ce6e86..3a3eefad08e 100644 --- a/src/v1/stage0/src/std_induction.rs +++ b/src/v1/stage0/src/std_induction.rs @@ -344,13 +344,13 @@ pub fn compose_sub_value_relations( callee_rel: Rc, ) -> Rc { match (*callee_rel.clone()).clone() { - SubValueRelation::PreservedValue => arg_rel, + SubValueRelation::PreservedValue => arg_rel.clone(), SubValueRelation::SubValueUnknown => Rc::new(SubValueRelation::SubValueUnknown), - SubValueRelation::NonIncreasingValue => match (*arg_rel).clone() { + SubValueRelation::NonIncreasingValue => match (*arg_rel.clone()).clone() { SubValueRelation::SubValueUnknown => Rc::new(SubValueRelation::SubValueUnknown), _ => Rc::new(SubValueRelation::NonIncreasingValue), }, - SubValueRelation::StrictAxisErased => match (*arg_rel).clone() { + SubValueRelation::StrictAxisErased => match (*arg_rel.clone()).clone() { SubValueRelation::SubValueUnknown => Rc::new(SubValueRelation::SubValueUnknown), SubValueRelation::PreservedValue => Rc::new(SubValueRelation::StrictAxisErased), SubValueRelation::StrictSubValue { .. } => Rc::new(SubValueRelation::StrictAxisErased), @@ -364,12 +364,12 @@ pub fn compose_sub_value_relations( SubValueRelation::NonIncreasingValue => Rc::new(SubValueRelation::NonIncreasingValue), SubValueRelation::MixedTop => Rc::new(SubValueRelation::MixedTop), }, - SubValueRelation::MixedTop => match (*arg_rel).clone() { + SubValueRelation::MixedTop => match (*arg_rel.clone()).clone() { SubValueRelation::SubValueUnknown => Rc::new(SubValueRelation::SubValueUnknown), SubValueRelation::NonIncreasingValue => Rc::new(SubValueRelation::NonIncreasingValue), _ => Rc::new(SubValueRelation::MixedTop), }, - SubValueRelation::StrictSubValue { .. } => match (*arg_rel).clone() { + SubValueRelation::StrictSubValue { .. } => match (*arg_rel.clone()).clone() { SubValueRelation::PreservedValue => callee_rel.clone(), SubValueRelation::SubValueUnknown => Rc::new(SubValueRelation::SubValueUnknown), SubValueRelation::StrictSubValue { field: f, .. } => { @@ -391,7 +391,7 @@ pub fn compose_sub_value_relations( SubValueRelation::StrictAxisErased => Rc::new(SubValueRelation::StrictAxisErased), SubValueRelation::MixedTop => Rc::new(SubValueRelation::MixedTop), }, - SubValueRelation::IteratedSubValue { field: _, .. } => match (*arg_rel).clone() { + SubValueRelation::IteratedSubValue { field: _, .. } => match (*arg_rel.clone()).clone() { SubValueRelation::PreservedValue => callee_rel.clone(), SubValueRelation::SubValueUnknown => Rc::new(SubValueRelation::SubValueUnknown), SubValueRelation::StrictSubValue { field: f, .. } => { @@ -417,7 +417,7 @@ pub fn compose_sub_value_relations( param: p, factor: f, .. - } => match (*arg_rel).clone() { + } => match (*arg_rel.clone()).clone() { SubValueRelation::PreservedValue => Rc::new(SubValueRelation::ArithmeticDescent { param: p.clone(), factor: f.clone(), @@ -962,27 +962,25 @@ pub fn cost_bound_params_of_factors( pub fn cost_bound_params_into(b: Rc, seen: Rc>) -> Rc> { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || match (*b.clone()).clone() { - CostBound::ConstantBound => seen, + CostBound::ConstantBound => seen.clone(), CostBound::AtomicBound { cost: c, .. } => { - cost_bound_params_of_factors(Rc::new(vec![c.clone()]), seen) + cost_bound_params_of_factors(Rc::new(vec![c.clone()]), seen.clone()) } CostBound::ProductBound { factors: fs, .. } => { - cost_bound_params_of_factors(fs.clone(), seen) + cost_bound_params_of_factors(fs.clone(), seen.clone()) } CostBound::SumOfProductsBound { terms: ts, .. } => ts.iter().cloned().fold( - seen, + seen.clone(), |acc: Rc>, factors: Rc>>| { cost_bound_params_of_factors(factors.clone(), acc) }, ), - CostBound::SumBound { terms: ts, .. } => ts - .iter() - .cloned() - .fold(seen, |acc: Rc>, inner: Rc| { - cost_bound_params_into(inner.clone(), acc) - }), - CostBound::ForeverBound => seen, - CostBound::ErrorBound => seen, + CostBound::SumBound { terms: ts, .. } => ts.iter().cloned().fold( + seen.clone(), + |acc: Rc>, inner: Rc| cost_bound_params_into(inner.clone(), acc), + ), + CostBound::ForeverBound => seen.clone(), + CostBound::ErrorBound => seen.clone(), }) } diff --git a/src/v1/stage0/src/std_nat.rs b/src/v1/stage0/src/std_nat.rs index 19188c278f1..fab9d414e54 100644 --- a/src/v1/stage0/src/std_nat.rs +++ b/src/v1/stage0/src/std_nat.rs @@ -49,7 +49,7 @@ pub fn nat_range_inclusive(lo: Nat, hi: Nat) -> Rc> { pub fn nat_range_accumulate(mut lo: Nat, mut hi: Nat, mut acc: Rc>) -> Rc> { loop { if (lo.clone() > hi.clone()) { - break acc; + break acc.clone(); } else { { let __tco_0 = (lo.clone() + 1); diff --git a/src/v1/stage0/src/std_occurrence_identity.rs b/src/v1/stage0/src/std_occurrence_identity.rs index f74e96b5526..91bd9515646 100644 --- a/src/v1/stage0/src/std_occurrence_identity.rs +++ b/src/v1/stage0/src/std_occurrence_identity.rs @@ -646,9 +646,9 @@ pub fn occurrence_transport_role_index_build( }, ); match declaration_build.refusal.clone() { - Some(_) => declaration_build, + Some(_) => declaration_build.clone(), std::option::Option::None => references.iter().cloned().fold( - declaration_build, + declaration_build.clone(), |build: Rc, reference: Rc| match build.refusal.clone() { Some(_) => build.clone(), diff --git a/src/v1/stage0/src/std_termination.rs b/src/v1/stage0/src/std_termination.rs index 8beca32d24e..cada7aed12e 100644 --- a/src/v1/stage0/src/std_termination.rs +++ b/src/v1/stage0/src/std_termination.rs @@ -51,8 +51,8 @@ pub fn descent_evidence_lattice_meet(a: DescentEvidence, b: DescentEvidence) -> pub fn descent_evidence_lattice_join(a: DescentEvidence, b: DescentEvidence) -> DescentEvidence { match a.clone() { - DescentEvidence::DescentUnknown => b, - DescentEvidence::NonIncreasing => match b { + DescentEvidence::DescentUnknown => b.clone(), + DescentEvidence::NonIncreasing => match b.clone() { DescentEvidence::Strict => DescentEvidence::Strict, _ => DescentEvidence::NonIncreasing, }, @@ -88,8 +88,8 @@ pub fn optional_evidence_meet( b: Option, ) -> Option { match a.clone() { - std::option::Option::None => b, - Some(va) => match b { + std::option::Option::None => b.clone(), + Some(va) => match b.clone() { std::option::Option::None => a.clone(), Some(vb) => Some(descent_evidence_lattice_meet(va.clone(), vb.clone())), }, diff --git a/src/v1/stage0/src/v1_compiler_compile.rs b/src/v1/stage0/src/v1_compiler_compile.rs index 2fc83e09097..c04722a4978 100644 --- a/src/v1/stage0/src/v1_compiler_compile.rs +++ b/src/v1/stage0/src/v1_compiler_compile.rs @@ -2868,8 +2868,8 @@ pub enum CensusModuleRetention { pub fn census_retain_module(module: Rc, retention: CensusModuleRetention) -> Rc { match retention.clone() { - CensusModuleRetention::RetainParsedModules => module, - CensusModuleRetention::RetainDeclarationHeads => census_heads_module_node(module), + CensusModuleRetention::RetainParsedModules => module.clone(), + CensusModuleRetention::RetainDeclarationHeads => census_heads_module_node(module.clone()), } } diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index 1cf3340cef7..29f3eb7fe52 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -1738,13 +1738,13 @@ pub fn render_named_type_base( }; let param_names = v1_rt::concat(explicit_params.clone(), inferred_params.clone()); if ((param_names.clone().len() as i64) == 0) { - base + base.clone() } else { { let spec = crate::v1_compiler_emit_core_support::language_spec(target.clone()); v1_rt::concat( v1_rt::concat( - v1_rt::concat(base, spec.type_arg_open.clone()), + v1_rt::concat(base.clone(), spec.type_arg_open.clone()), param_names.clone().join(&", ".to_string()), ), spec.type_arg_close.clone(), @@ -3874,14 +3874,14 @@ pub fn shared_tco_block( std::option::Option::None => spec.tco.clone().break_return.clone(), }; if ((init_state.text.clone().len() as i64) == 0) { - last_str + last_str.clone() } else { v1_rt::concat( v1_rt::concat( init_state.text.clone().join(&"\n".to_string()), "\n".to_string(), ), - last_str, + last_str.clone(), ) } } @@ -4582,14 +4582,14 @@ pub fn emit_unified_typed_func_body( ), }; if ((init_state.text.clone().len() as i64) == 0) { - last_str + last_str.clone() } else { v1_rt::concat( v1_rt::concat( init_state.text.clone().join(&"\n".to_string()), "\n".to_string(), ), - last_str, + last_str.clone(), ) } } @@ -4800,7 +4800,7 @@ pub fn hex_digit_char(d: i64) -> String { pub fn int_to_upper_hex_inner(mut n: i64, mut acc: String) -> String { loop { if (n.clone() == 0) { - break acc; + break acc.clone(); } else { { let __tco_0 = (n.clone() / 16); @@ -4831,20 +4831,20 @@ pub fn escape_emoji_codepoints_inner( ) -> String { loop { if (pos.clone() >= n.clone()) { - break acc; + break acc.clone(); } else { let ch_str = v1_rt::char_at(&s, pos.clone()); let cp = v1_rt::code_point(ch_str.clone()); let next_acc = if v1_rt::is_emoji_ident(cp.clone()) { v1_rt::concat( v1_rt::concat( - v1_rt::concat(acc, prefix.clone()), + v1_rt::concat(acc.clone(), prefix.clone()), int_to_upper_hex(cp.clone()), ), suffix.clone(), ) } else { - v1_rt::concat(acc, ch_str.clone()) + v1_rt::concat(acc.clone(), ch_str.clone()) }; { let __tco_0 = (pos + 1); @@ -4872,9 +4872,9 @@ pub fn apply_char_sanitization(name: String, rule: CharSanitization) -> String { { let esc = crate::v1_compiler_languages::canonical_emoji_char_escape(); match rule.clone() { - CharSanitization::NoCharSanitization => name, + CharSanitization::NoCharSanitization => name.clone(), CharSanitization::EmojiEscape => { - escape_emoji_codepoints(name, esc.prefix.clone(), esc.suffix.clone()) + escape_emoji_codepoints(name.clone(), esc.prefix.clone(), esc.suffix.clone()) } } } @@ -6009,7 +6009,7 @@ pub fn emit_typed_cast_shared( _ => "".to_string(), }; if ((src_ty.clone() != "".to_string()) && (src_ty.clone() == ty_str.clone())) { - expr_str + expr_str.clone() } else { if cast_source_representation_identical( expr.clone(), @@ -6017,7 +6017,7 @@ pub fn emit_typed_cast_shared( env.clone(), target.clone(), ) { - expr_str + expr_str.clone() } else { if crate::v1_compiler_coercion::can_cast( target.clone(), @@ -6025,7 +6025,7 @@ pub fn emit_typed_cast_shared( ty_str.clone(), ) { crate::v1_compiler_coercion::render_cast( - expr_str, + expr_str.clone(), ty_str.clone(), target.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 f934098f77c..bad936adee7 100644 --- a/src/v1/stage0/src/v1_compiler_emit_core_support.rs +++ b/src/v1/stage0/src/v1_compiler_emit_core_support.rs @@ -205,7 +205,7 @@ pub fn to_string(value: i64) -> String { pub fn to_string_helper(mut value: i64, mut acc: Rc>) -> Rc> { loop { if (value.clone() == 0) { - break acc; + break acc.clone(); } else { let rest = (value.clone() / 10); let digit = (value.clone() - (rest.clone() * 10)); @@ -538,7 +538,7 @@ pub fn apply_named_template_nested( ) -> String { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { match keys.clone().first().cloned() { - std::option::Option::None => template, + std::option::Option::None => template.clone(), Some(key) => { let rest = Rc::new( keys.clone() @@ -553,6 +553,7 @@ pub fn apply_named_template_nested( Some(val) => { let parts = Rc::new( template + .clone() .split(&placeholder.clone()) .map(|s| s.to_string()) .collect::>(), diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 7717f67c09f..b3808dcc482 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -6108,14 +6108,14 @@ pub fn maybe_mark_shared_type( } => (unit_only.clone() == false), }); if is_grounded_coproduct_native_alias(summary.name.clone()) { - acc + acc.clone() } else { if (needs_sharing.clone() && !is_type_constant(summary.clone(), recursive_type_set.clone())) { - v1_rt::rc_set_insert(acc, summary.name.clone()) + v1_rt::rc_set_insert(acc.clone(), summary.name.clone()) } else { - acc + acc.clone() } } } @@ -16207,11 +16207,11 @@ pub fn emit_type_def_from_connective( emit_info.clone(), ); if (clone_bound_refusal.clone() == "".to_string()) { - struct_text + struct_text.clone() } else { v1_rt::concat( v1_rt::concat(clone_bound_refusal.clone(), "\n".to_string()), - struct_text, + struct_text.clone(), ) } } @@ -16394,11 +16394,11 @@ pub fn emit_type_def_from_connective( } }; if (validations.clone() == "".to_string()) { - enum_text + enum_text.clone() } else { v1_rt::concat( v1_rt::concat(validations.clone(), "\n".to_string()), - enum_text, + enum_text.clone(), ) } } @@ -16766,9 +16766,9 @@ pub fn emit_struct_from_children( }; let with_accessors = v1_rt::concat(struct_def.clone(), sealed_accessors.clone()); if (surface.impl_bodies.clone() == "".to_string()) { - with_accessors + with_accessors.clone() } else { - v1_rt::concat(with_accessors, surface.impl_bodies.clone()) + v1_rt::concat(with_accessors.clone(), surface.impl_bodies.clone()) } } } @@ -17367,9 +17367,9 @@ pub fn emit_enum_from_children( ) }; if (supplemental.clone() == "".to_string()) { - with_accessors + with_accessors.clone() } else { - v1_rt::concat(with_accessors, supplemental.clone()) + v1_rt::concat(with_accessors.clone(), supplemental.clone()) } } } @@ -19748,14 +19748,14 @@ pub fn emit_func_body( std::option::Option::None => "Ok(())".to_string(), }; if ((init_state.text.clone().len() as i64) == 0) { - last_str + last_str.clone() } else { v1_rt::concat( v1_rt::concat( init_state.text.clone().join(&"\n".to_string()), "\n".to_string(), ), - last_str, + last_str.clone(), ) } } @@ -20715,7 +20715,7 @@ pub fn emit_variant_pattern( } } } - std::option::Option::None => qualified, + std::option::Option::None => qualified.clone(), } } else { if ((field_bindings.clone().len() as i64) == 1) { @@ -20736,7 +20736,7 @@ pub fn emit_variant_pattern( if is_string_lit_pattern(fb_pat.clone()) { v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, "(ref ".to_string()), + v1_rt::concat(qualified.clone(), "(ref ".to_string()), bind_name.clone(), ), ")".to_string(), @@ -20760,7 +20760,7 @@ pub fn emit_variant_pattern( ); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, "(".to_string()), + v1_rt::concat(qualified.clone(), "(".to_string()), inner_pat.clone(), ), ")".to_string(), @@ -20787,7 +20787,10 @@ pub fn emit_variant_pattern( )); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, " { ref ".to_string()), + v1_rt::concat( + qualified.clone(), + " { ref ".to_string(), + ), bind_name.clone(), ), ", .. }".to_string(), @@ -20811,7 +20814,7 @@ pub fn emit_variant_pattern( ); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, " { ".to_string()), + v1_rt::concat(qualified.clone(), " { ".to_string()), v1_rt::concat( v1_rt::concat( crate::v1_compiler_emit::emit_ident( @@ -20830,12 +20833,12 @@ pub fn emit_variant_pattern( } } } - std::option::Option::None => qualified, + std::option::Option::None => qualified.clone(), } } else { if ((field_bindings.clone().len() as i64) == 0) { variant_pattern_shape_str( - qualified, + qualified.clone(), rust_name.clone(), resolved_parent.clone(), emit_info.clone(), @@ -20858,7 +20861,7 @@ pub fn emit_variant_pattern( }); if ((effective_bindings.clone().len() as i64) == 0) { variant_pattern_shape_str( - qualified, + qualified.clone(), rust_name.clone(), resolved_parent.clone(), emit_info.clone(), @@ -20909,7 +20912,7 @@ v1_rt::concat(v1_rt::concat(crate::v1_compiler_emit::emit_ident(fb_name.clone(), let bindings_str = binding_strs.clone().join(&", ".to_string()); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, " { ".to_string()), + v1_rt::concat(qualified.clone(), " { ".to_string()), bindings_str.clone(), ), ", .. }".to_string(), @@ -21358,7 +21361,7 @@ pub fn emit_variant_pattern_rc_aware( } } } - std::option::Option::None => qualified, + std::option::Option::None => qualified.clone(), } } else { if ((field_bindings.clone().len() as i64) == 1) { @@ -21379,7 +21382,7 @@ pub fn emit_variant_pattern_rc_aware( if is_string_lit_pattern(fb_pat.clone()) { v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, "(ref ".to_string()), + v1_rt::concat(qualified.clone(), "(ref ".to_string()), bind_name.clone(), ), ")".to_string(), @@ -21411,7 +21414,7 @@ pub fn emit_variant_pattern_rc_aware( ); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, "(".to_string()), + v1_rt::concat(qualified.clone(), "(".to_string()), inner_pat.clone(), ), ")".to_string(), @@ -21438,7 +21441,10 @@ pub fn emit_variant_pattern_rc_aware( )); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, " { ref ".to_string()), + v1_rt::concat( + qualified.clone(), + " { ref ".to_string(), + ), bind_name.clone(), ), ", .. }".to_string(), @@ -21448,7 +21454,10 @@ pub fn emit_variant_pattern_rc_aware( if field_needs_rc_ref(fb_name.clone(), rc_analysis.clone()) { v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, " { ref ".to_string()), + v1_rt::concat( + qualified.clone(), + " { ref ".to_string(), + ), crate::v1_compiler_emit::emit_ident( fb_name.clone(), RenderTarget::Rust, @@ -21482,7 +21491,10 @@ pub fn emit_variant_pattern_rc_aware( ); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, " { ".to_string()), + v1_rt::concat( + qualified.clone(), + " { ".to_string(), + ), v1_rt::concat( v1_rt::concat( crate::v1_compiler_emit::emit_ident( @@ -21502,12 +21514,12 @@ pub fn emit_variant_pattern_rc_aware( } } } - std::option::Option::None => qualified, + std::option::Option::None => qualified.clone(), } } else { if ((field_bindings.clone().len() as i64) == 0) { variant_pattern_shape_str( - qualified, + qualified.clone(), rust_name.clone(), resolved_parent.clone(), emit_info.clone(), @@ -21530,7 +21542,7 @@ pub fn emit_variant_pattern_rc_aware( }); if ((effective_bindings.clone().len() as i64) == 0) { variant_pattern_shape_str( - qualified, + qualified.clone(), rust_name.clone(), resolved_parent.clone(), emit_info.clone(), @@ -21586,7 +21598,7 @@ v1_rt::concat(v1_rt::concat(crate::v1_compiler_emit::emit_ident(fb_name.clone(), let bindings_str = binding_strs.clone().join(&", ".to_string()); v1_rt::concat( v1_rt::concat( - v1_rt::concat(qualified, " { ".to_string()), + v1_rt::concat(qualified.clone(), " { ".to_string()), bindings_str.clone(), ), ", .. }".to_string(), @@ -33124,14 +33136,14 @@ pub fn emit_rust_tco_block( std::option::Option::None => "break;".to_string(), }; if ((init_state.text.clone().len() as i64) == 0) { - last_str + last_str.clone() } else { v1_rt::concat( v1_rt::concat( init_state.text.clone().join(&"\n".to_string()), "\n".to_string(), ), - last_str, + last_str.clone(), ) } } @@ -39819,13 +39831,15 @@ pub fn cli_option_rust_type(opt: Rc) -> String { CliOptionValue::CliMillisecondValue { .. } => "u64".to_string(), }; match opt.arity.clone() { - CliOptionArity::CliRequired => base, - CliOptionArity::CliAtMostOne => { - v1_rt::concat(v1_rt::concat("Option<".to_string(), base), ">".to_string()) - } - CliOptionArity::CliRepeated => { - v1_rt::concat(v1_rt::concat("Vec<".to_string(), base), ">".to_string()) - } + CliOptionArity::CliRequired => base.clone(), + CliOptionArity::CliAtMostOne => v1_rt::concat( + v1_rt::concat("Option<".to_string(), base.clone()), + ">".to_string(), + ), + CliOptionArity::CliRepeated => v1_rt::concat( + v1_rt::concat("Vec<".to_string(), base.clone()), + ">".to_string(), + ), } } } diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 1080dd6fde2..d0a111aecf2 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -6600,7 +6600,7 @@ pub fn borrowed_callable_call_type( source_indices.clone(), ); if ((tp_names.clone().len() as i64) == 0) { - base + base.clone() } else { { let value_params = Rc::new({ @@ -6669,7 +6669,7 @@ pub fn borrowed_callable_call_type( } }, ); - substitute_generics(base, subst.clone(), source_indices.clone()) + substitute_generics(base.clone(), subst.clone(), source_indices.clone()) } } } @@ -13321,9 +13321,9 @@ pub fn alias_chain_target_after_args( pub fn alias_chain_carrier(n: Rc) -> Rc { if ((n.connective.clone() == Connective::NoConnective) && (n.name.clone() != "".to_string())) { - n + n.clone() } else { - structural_from_expanded_type(n) + structural_from_expanded_type(n.clone()) } } @@ -14831,10 +14831,10 @@ pub fn maybe_insert_composed_field_relation( ) -> Rc>> { match composed.clone() { Some(rel) => match (*rel.clone()).clone() { - SubValueRelation::SubValueUnknown => acc, - _ => v1_rt::rc_map_insert(acc, fname.clone(), rel.clone()), + SubValueRelation::SubValueUnknown => acc.clone(), + _ => v1_rt::rc_map_insert(acc.clone(), fname.clone(), rel.clone()), }, - std::option::Option::None => acc, + std::option::Option::None => acc.clone(), } } @@ -20357,9 +20357,9 @@ pub fn substitute_generics( source_indices: Rc>>, ) -> Rc { if v1_rt::map_is_empty(&subst) { - n + n.clone() } else { - substitute_generics_apply(n, subst.clone(), source_indices.clone()) + substitute_generics_apply(n.clone(), subst.clone(), source_indices.clone()) } } diff --git a/src/v1/stage0/src/v1_compiler_infer_patterns.rs b/src/v1/stage0/src/v1_compiler_infer_patterns.rs index 94f6d1c6b46..518f62b6097 100644 --- a/src/v1/stage0/src/v1_compiler_infer_patterns.rs +++ b/src/v1/stage0/src/v1_compiler_infer_patterns.rs @@ -1109,11 +1109,11 @@ pub fn render_constructor_witness( }, ); if ((parts.clone().len() as i64) == 0) { - ctor + ctor.clone() } else { v1_rt::concat( v1_rt::concat( - v1_rt::concat(ctor, " { ".to_string()), + v1_rt::concat(ctor.clone(), " { ".to_string()), parts.clone().join(&", ".to_string()), ), " }".to_string(), diff --git a/src/v1/stage0/src/v1_compiler_infer_resolve.rs b/src/v1/stage0/src/v1_compiler_infer_resolve.rs index 2bc11fa1088..fdb0ea927e7 100644 --- a/src/v1/stage0/src/v1_compiler_infer_resolve.rs +++ b/src/v1/stage0/src/v1_compiler_infer_resolve.rs @@ -429,12 +429,12 @@ pub fn resolve_paramless_generic_use_decl( brand: String, ) -> Rc { if ((n.children.clone().len() as i64) == 0) { - decl + decl.clone() } else { resolve_paramless_generic_use_decl_with_children( env.clone(), n.clone(), - decl, + decl.clone(), brand.clone(), ) } diff --git a/src/v1/stage0/src/v1_compiler_infer_types.rs b/src/v1/stage0/src/v1_compiler_infer_types.rs index 5cf6fbe16f9..e8e3bf8db53 100644 --- a/src/v1/stage0/src/v1_compiler_infer_types.rs +++ b/src/v1/stage0/src/v1_compiler_infer_types.rs @@ -2124,10 +2124,10 @@ pub fn node_type_shape_argument_list( .collect::>(), ); if ((rest.clone().len() as i64) == 0) { - head + head.clone() } else { v1_rt::concat( - head, + head.clone(), v1_rt::concat( ",".to_string(), node_type_shape_argument_list(rest.clone(), source_indices.clone()), @@ -3031,17 +3031,17 @@ pub fn extract_optional_inner_node(n: Rc) -> Rc { { let is_optional = (n.return_cardinality.clone() == Cardinality::CardOptional); if is_optional.clone() { - crate::v1_std_core::with_required_cardinality(n) + crate::v1_std_core::with_required_cardinality(n.clone()) } else { if ((n.name.clone() == "Optional".to_string()) && ((n.children.clone().len() as i64) == 1)) { match n.children.clone().first().cloned() { Some(inner) => inner.clone(), - std::option::Option::None => n, + std::option::Option::None => n.clone(), } } else { - n + n.clone() } } } diff --git a/src/v1/stage0/src/v1_compiler_ownership.rs b/src/v1/stage0/src/v1_compiler_ownership.rs index 9685edc7d7b..da543a6ef75 100644 --- a/src/v1/stage0/src/v1_compiler_ownership.rs +++ b/src/v1/stage0/src/v1_compiler_ownership.rs @@ -194,10 +194,49 @@ pub fn record_use( } pub fn max_usage_by_fan_out(a: Rc, b: Rc) -> Rc { - if (binding_fan_out(b.clone()) > binding_fan_out(a.clone())) { - b.clone() - } else { - a.clone() + { + let b_is_larger = (binding_fan_out(b.clone()) > binding_fan_out(a.clone())); + let selected = if b_is_larger.clone() { + b.clone() + } else { + a.clone() + }; + let other = if b_is_larger.clone() { + a.clone() + } else { + b.clone() + }; + let missing_reads = Rc::new({ + let mut __result = Vec::new(); + for edge in other.consumers.clone().iter().cloned() { + if match edge.kind.clone() { + EdgeKind::Read => { + ({ + let mut __found = false; + for existing in selected.consumers.clone().iter().cloned() { + if (((existing.kind.clone() == EdgeKind::Read) + && (existing.span_start.clone() == edge.span_start.clone())) + && (existing.site.clone() == edge.site.clone())) + { + __found = true; + break; + } + } + __found + } == false) + } + _ => false, + } { + __result.push(edge); + } + } + __result + }); + Rc::new(BindingUsage { + name: selected.name.clone(), + binding_kind: selected.binding_kind.clone(), + consumers: v1_rt::concat(selected.consumers.clone(), missing_reads.clone()), + }) } } diff --git a/src/v1/stage0/src/v1_compiler_parse.rs b/src/v1/stage0/src/v1_compiler_parse.rs index a2cfdd11bc0..75b3489d42a 100644 --- a/src/v1/stage0/src/v1_compiler_parse.rs +++ b/src/v1/stage0/src/v1_compiler_parse.rs @@ -3772,7 +3772,7 @@ pub fn last_consumed_token_end( ) -> Option { loop { if (from.clone() >= until.clone()) { - break end; + break end.clone(); } else { match all.clone().get((from.clone()) as usize).cloned() { std::option::Option::None => { @@ -9675,7 +9675,7 @@ pub fn int_to_string(value: i64) -> String { pub fn int_to_string_acc(mut value: i64, mut acc: Rc>) -> Rc> { loop { if (value.clone() == 0) { - break acc; + break acc.clone(); } else { let rest = (value.clone() / 10); let digit = (value.clone() - (rest.clone() * 10)); @@ -15417,7 +15417,7 @@ pub fn parse_let(tokens: Rc, ctx: Rc) -> Rc, ctx: Rc) -> Rc Rc>>> { loop { if (remaining.clone() <= 0) { - break bounds; + break bounds.clone(); } else { let round = generic_type_names.iter().cloned().fold( Rc::new(CloneBoundRound { - bounds: bounds, + bounds: bounds.clone(), added: 0, }), |acc: Rc, type_name: String| match v1_rt::map_get( diff --git a/src/v1/stage0/tests/ownership_branch_read.rs b/src/v1/stage0/tests/ownership_branch_read.rs new file mode 100644 index 00000000000..c1ada64b6ea --- /dev/null +++ b/src/v1/stage0/tests/ownership_branch_read.rs @@ -0,0 +1,124 @@ +//! Ownership branch-join controls on the emitted seed. +//! Execute: cargo test -p v1-compiler --test ownership_branch_read. +//! Required clippy --all-targets compiles this target; no CI step executes it. +//! The declared drop is gunbc.rung_drop.rust_unit_tests_off_the_merge_path. +//! Return the fixtures to .dag when a required seed-rooted claim phase supplies +//! same-identity, candidate-bound terminals to the disposition receipt. + +use std::rc::Rc; +use v1_compiler::v1_compiler_artifact::RenderTarget; +use v1_compiler::v1_compiler_compile::{compile_sources, SourceFile}; +use v1_compiler::v1_compiler_ownership::{ + build_movable_set, max_usage_by_fan_out, BindingUsage, EdgeClassification, EdgeKind, + OwnershipProof, +}; + +fn usage(edges: &[(EdgeKind, &str, i64)]) -> Rc { + Rc::new(BindingUsage { + name: "item".into(), + binding_kind: None, + consumers: Rc::new( + edges + .iter() + .map(|(kind, site, span)| { + Rc::new(EdgeClassification { + kind: *kind, + site: (*site).into(), + span_start: *span, + }) + }) + .collect(), + ), + }) +} + +fn movable(binding: Rc) -> bool { + let proof = Rc::new(OwnershipProof { + func_name: "fixture".into(), + bindings: Rc::new(im::hashmap! { "item".into() => binding }), + decisions: Rc::new(im::Vector::new()), + fold_acc_unwrap: Rc::new(im::Vector::new()), + }); + build_movable_set(proof, Rc::new(im::ordset! { "item".into() })).contains("item") +} + +#[test] +fn branch_read_survives_larger_projection_path() { + use EdgeKind::{Consumed, Projected, Read}; + let read_path = usage(&[(Consumed, "return", 1), (Read, "read", 2)]); + let projection_path = usage(&[ + (Consumed, "return", 1), + (Projected, ".a", 3), + (Projected, ".b", 4), + ]); + assert!(!movable(max_usage_by_fan_out(read_path, projection_path))); +} + +#[test] +fn projected_before_consumed_stays_movable() { + use EdgeKind::{Consumed, Projected}; + let path = usage(&[(Projected, ".a", 1), (Consumed, "return", 2)]); + assert!(movable(max_usage_by_fan_out(path.clone(), path))); +} + +#[test] +fn whole_read_then_consume_stays_non_movable() { + use EdgeKind::{Consumed, Read}; + let path = usage(&[(Read, "read", 1), (Consumed, "return", 2)]); + assert!(!movable(max_usage_by_fan_out(path.clone(), path))); +} + +#[test] +fn authored_constructor_orders_emit_clones() { + for (case, early, fields) in [ + ( + "baseline", + "", + "child: Present { value: item }, text: item.text", + ), + ( + "move_first", + "if early { return item }", + "child: Present { value: item }, text: item.text", + ), + ( + "borrow_first", + "if early { return item }", + "text: item.text, child: Present { value: item }", + ), + ] { + let source = format!( + "module move_probe\ntype Item {{ child: Item? text: String other: String }}\n\ + fn build(item: Item, early: Bool) -> Item {{ {early} if early {{\ + Item {{ {fields}, other: \"\" }} }} else {{\ + Item {{ child: item.child, text: item.text, other: item.other }} }} }}" + ); + let result = compile_sources( + Rc::new(im::vector![Rc::new(SourceFile { + path: "move_probe.dag".into(), + content: source + })]), + RenderTarget::Rust, + ); + assert!( + result.diagnostics.is_empty(), + "{case}: {:?}", + result.diagnostics + ); + let module = result + .files + .iter() + .find(|file| file.content.contains("pub fn build(")) + .expect("emitted probe module"); + assert!( + module.content.contains("child: Some(item.clone())"), + "{case}: {}", + module.content + ); + assert!( + module.content.contains("text: item.text.clone()"), + "{case}: {}", + module.content + ); + } +}