diff --git a/src/v2/04_infer.dag b/src/v2/04_infer.dag index 0be05e71a8f..cae52115554 100644 --- a/src/v2/04_infer.dag +++ b/src/v2/04_infer.dag @@ -3852,13 +3852,15 @@ fn classify_body_provenance(expr: Node, param_names: List, param_types: match expr.expr_data { ExprVar { binding_kind: _ } => let vname = expr_var_name_at(texpr: expr, source_indices: type_env.source_indices) - if param_names |> any(pn => pn == vname) { - map_insert(empty_map(), vname, PreservedValue) - } else { - match map_get(let_prov, vname) { - Some { value: prov } => prov - None => empty_map() - } + // Check let_prov first: locals shadow params (Track 3 limitation). + match map_get(let_prov, vname) { + Some { value: prov } => prov + None => + if param_names |> any(pn => pn == vname) { + map_insert(empty_map(), vname, PreservedValue) + } else { + empty_map() + } } ExprFieldAccess { summary: _ } => @@ -3895,9 +3897,51 @@ fn classify_body_provenance(expr: Node, param_names: List, param_types: compose_callee_provenance(callee: mname, arg_by_name: method_call_args_by_name(call: expr, func_sigs: func_sigs, source_indices: type_env.source_indices), param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: let_prov) ExprMatch => + let scrut = match_scrutinee(texpr: expr) + let scrut_prov = classify_body_provenance(expr: scrut, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: let_prov) let arms = match_arm_nodes(texpr: expr) let arm_provs = arms |> map(arm => - classify_body_provenance(expr: arm_body(n: arm), param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: let_prov) + // Extend let_prov with arm bindings composed through inductive fields. + // Uses field_binding_pattern (Bind { name }) for the introduced binding name, + // field_binding_name_at for the field label in inductive field lookup. + // Includes variant_name in the filter. Fails closed on unmatched fields. + let arm_let_prov = match arm_pattern(n: arm) { + VariantPattern { name: vname, parent_enum: _, field_bindings: bindings } => + fold(bindings, init: let_prov, f: (acc, binding) => + let field_label = field_binding_name_at(n: binding, source_indices: type_env.source_indices) + let bind_name = match field_binding_pattern(n: binding) { + Bind { name: bn } => bn + _ => "" + } + if bind_name != "" && field_label != "" { + let field_prov = fold(map_keys(scrut_prov), init: empty_prov_map(), f: (fp_acc, pname) => + match map_get(scrut_prov, pname) { + Some { value: scrut_rel } => + let lookup_type = match scrut_rel { + PreservedValue => match map_get(param_types, pname) { Some { value: t } => t None => "" } + StrictSubValue { field: f, factor: _ } => f.element_type + IteratedSubValue { field: f } => f.element_type + _ => "" + } + if lookup_type != "" { + let ind_fields = inductive_fields_for(env: type_env, type_name: lookup_type) + let matching = ind_fields |> filter(f => f.field_name == field_label && (f.variant_name == vname || f.variant_name == "")) |> first + match matching { + Some { value: ind_field } => map_insert(fp_acc, pname, compose_sub_value(base: scrut_rel, field: ind_field)) + None => fp_acc + } + } else { fp_acc } + None => fp_acc + } + ) + map_insert(acc, bind_name, field_prov) + } else { acc } + ) + Bind { name: bname } => + map_insert(let_prov, bname, scrut_prov) + _ => let_prov + } + classify_body_provenance(expr: arm_body(n: arm), param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: arm_let_prov) ) meet_body_provenance_maps(provs: arm_provs) @@ -3922,9 +3966,37 @@ fn classify_body_provenance(expr: Node, param_names: List, param_types: } ExprBlock => - match expr.children |> last { - Some { value: last_stmt } => - classify_body_provenance(expr: last_stmt, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: let_prov) + // Fold over block statements: extend let_prov for block-level let-bindings, + // return last statement's provenance. Let-with-body restores outer scope + // so the binding does not leak into subsequent statements. + let block_result = fold(expr.children, init: BlockProvFoldState { prov: let_prov, last: empty_prov_map() }, f: (state, stmt) => + match stmt.expr_data { + ExprLet => + let bname = let_binding_name_at(texpr: stmt, source_indices: type_env.source_indices) + let value = let_value(texpr: stmt) + let value_prov = classify_body_provenance(expr: value, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: state.prov) + let updated_prov = map_insert(state.prov, bname, value_prov) + match let_body(texpr: stmt) { + Some { value: body } => + // Let-with-body: binding scoped to body only; restore state.prov after. + BlockProvFoldState { prov: state.prov, last: classify_body_provenance(expr: body, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: updated_prov) } + None => + // Block-level let: binding persists for subsequent statements. + BlockProvFoldState { prov: updated_prov, last: value_prov } + } + _ => + BlockProvFoldState { prov: state.prov, last: classify_body_provenance(expr: stmt, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: state.prov) } + } + ) + block_result.last + + ExprCast => + classify_body_provenance(expr: cast_expr(texpr: expr), param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: let_prov) + + ExprReturn => + match expr.children |> first { + Some { value: inner } => + classify_body_provenance(expr: inner, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: let_prov) None => empty_map() } @@ -3932,6 +4004,12 @@ fn classify_body_provenance(expr: Node, param_names: List, param_types: } } +// Accumulator for ExprBlock fold in classify_body_provenance. +type BlockProvFoldState { + prov: Map> + last: Map +} + // Build named-arg map for ExprCall: all children are named arg nodes. fn call_args_by_name(call: Node, source_indices: Map) -> Map { fold(call.children, init: empty_map(), f: (acc, arg_node) => diff --git a/src/v2/stage0/src/v2_compiler_infer.rs b/src/v2/stage0/src/v2_compiler_infer.rs index c0a6d0d7351..ef8fd3a2e39 100644 --- a/src/v2/stage0/src/v2_compiler_infer.rs +++ b/src/v2/stage0/src/v2_compiler_infer.rs @@ -4094,7 +4094,7 @@ if ((pname.clone().as_str() != "".to_string().as_str()) && (ptype.clone().as_str acc.clone() } }); -let scalar = classify_body_provenance(&body, param_names.clone(), ¶m_types, &type_env, &func_sigs, v2_rt::rc_empty_map::>>>()); +let scalar = classify_body_provenance(&body, ¶m_names, ¶m_types, &type_env, &func_sigs, &v2_rt::rc_empty_map::>>>()); if ((Rc::new(v2_rt::map_keys(&scalar)).len() as i64) > 0) { Rc::new(vec![scalar.clone()]) } else { @@ -4110,24 +4110,24 @@ if (((per_field.clone().len() as i64) > 0) && { let mut __found = false; for m i } } -pub fn classify_body_provenance(expr: &Rc, param_names: Rc>, param_types: &Rc>, type_env: &Rc, func_sigs: &Rc>>, let_prov: Rc>>>>) -> Rc>> { +pub fn classify_body_provenance(expr: &Rc, param_names: &Rc>, param_types: &Rc>, type_env: &Rc, func_sigs: &Rc>>, let_prov: &Rc>>>>) -> Rc>> { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { match (*expr.expr_data.clone()).clone() { ExprData::ExprVar { .. } => { let vname = expr_var_name_at(expr.clone(), type_env.source_indices.clone()); -if { let mut __found = false; for pn in param_names.clone().iter().cloned() { if (pn.clone().as_str() == vname.clone().as_str()) { __found = true; break; } } __found } { +match v2_rt::map_get(&let_prov, vname.clone()) { + Some(prov) => prov.clone(), + None => if { let mut __found = false; for pn in param_names.clone().iter().cloned() { if (pn.clone().as_str() == vname.clone().as_str()) { __found = true; break; } } __found } { v2_rt::rc_map_insert(v2_rt::rc_empty_map::>(), vname.clone(), Rc::new(SubValueRelation::PreservedValue)) } else { - match v2_rt::map_get(&let_prov, vname.clone()) { - Some(prov) => prov.clone(), - None => v2_rt::rc_empty_map::>(), + v2_rt::rc_empty_map::>() + }, } - } }, ExprData::ExprFieldAccess { .. } => { let base = field_access_base(expr.clone()); let field = field_access_field_at(expr.clone(), type_env.source_indices.clone()); -let base_prov = classify_body_provenance(&base, param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone()); +let base_prov = classify_body_provenance(&base, ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov); Rc::new(v2_rt::map_keys(&base_prov)).iter().cloned().fold(v2_rt::rc_empty_map::>(), |acc: Rc>>, pname: String| match v2_rt::map_get(&base_prov, pname.clone()) { Some(base_rel) => { let type_for_lookup = match (*base_rel.clone()).clone() { @@ -4164,15 +4164,63 @@ compose_callee_provenance(callee, call_args_by_name(expr.clone(), type_env.sourc compose_callee_provenance(mname, method_call_args_by_name(&expr, func_sigs.clone(), &type_env.source_indices.clone()), param_names.clone(), param_types.clone(), type_env.clone(), &func_sigs, let_prov.clone()) }, ExprData::ExprMatch => { - let arms = match_arm_nodes(expr.clone()); -let arm_provs = Rc::new({ let mut __result = Vec::new(); for arm in arms.iter().cloned() { __result.push(classify_body_provenance(&arm_body(&arm), param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone())); } __result }); + let scrut = match_scrutinee(expr.clone()); +let scrut_prov = classify_body_provenance(&scrut, ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov); +let arms = match_arm_nodes(expr.clone()); +let arm_provs = Rc::new({ let mut __result = Vec::new(); for arm in arms.iter().cloned() { __result.push({ + let arm_let_prov = match (*arm_pattern(arm.clone())).clone() { + MatchPattern::VariantPattern { name: vname, field_bindings: bindings, .. } => bindings.clone().iter().cloned().fold(let_prov.clone(), |acc: Rc>>>>, binding: Rc| { + let field_label = field_binding_name_at(binding.clone(), type_env.source_indices.clone()); +let bind_name = match (*field_binding_pattern(binding.clone())).clone() { + MatchPattern::Bind { name: bn, .. } => bn.clone(), + _ => "".to_string(), +}; +if ((bind_name.clone().as_str() != "".to_string().as_str()) && (field_label.clone().as_str() != "".to_string().as_str())) { + { + let field_prov = Rc::new(v2_rt::map_keys(&scrut_prov)).iter().cloned().fold(empty_prov_map(), |fp_acc: Rc>>, pname: String| match v2_rt::map_get(&scrut_prov, pname.clone()) { + Some(scrut_rel) => { + let lookup_type = match (*scrut_rel.clone()).clone() { + SubValueRelation::PreservedValue => match v2_rt::map_get(¶m_types, pname.clone()) { + Some(t) => t.clone(), + None => "".to_string(), +}, + SubValueRelation::StrictSubValue { field: f, .. } => f.element_type.clone(), + SubValueRelation::IteratedSubValue { field: f, .. } => f.element_type.clone(), + _ => "".to_string(), +}; +if (lookup_type.clone().as_str() != "".to_string().as_str()) { + { + let ind_fields = inductive_fields_for(type_env.clone(), lookup_type.clone()); +let matching = Rc::new({ let mut __result = Vec::new(); for f in ind_fields.clone().iter().cloned() { if ((f.field_name.clone().as_str() == field_label.clone().as_str()) && ((f.variant_name.clone().as_str() == vname.clone().as_str()) || (f.variant_name.clone().as_str() == "".to_string().as_str()))) { __result.push(f); } } __result }).first().cloned(); +match matching.clone() { + Some(ind_field) => v2_rt::rc_map_insert(fp_acc.clone(), pname.clone(), compose_sub_value(scrut_rel.clone(), ind_field.clone())), + None => fp_acc.clone(), +} +} + } else { + fp_acc.clone() + } +}, + None => fp_acc.clone(), +}); +v2_rt::rc_map_insert(acc.clone(), bind_name.clone(), field_prov.clone()) +} + } else { + acc.clone() + } +}), + MatchPattern::Bind { name: bname, .. } => v2_rt::rc_map_insert(let_prov.clone(), bname.clone(), scrut_prov.clone()), + _ => let_prov.clone(), +}; +classify_body_provenance(&arm_body(&arm), ¶m_names, ¶m_types, &type_env, &func_sigs, &arm_let_prov) +}); } __result }); meet_body_provenance_maps(&arm_provs) }, ExprData::ExprIf => { - let then_prov = classify_body_provenance(&if_then_branch(expr.clone()), param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone()); + let then_prov = classify_body_provenance(&if_then_branch(expr.clone()), ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov); match if_else_branch(expr.clone()) { Some(else_expr) => { - let else_prov = classify_body_provenance(&else_expr, param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone()); + let else_prov = classify_body_provenance(&else_expr, ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov); meet_body_provenance_maps(&Rc::new(vec![then_prov, else_prov])) }, None => then_prov, @@ -4181,15 +4229,44 @@ meet_body_provenance_maps(&Rc::new(vec![then_prov, else_prov])) ExprData::ExprLet => { let value = let_value(expr.clone()); let bname = let_binding_name_at(expr.clone(), type_env.source_indices.clone()); -let value_prov = classify_body_provenance(&value, param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone()); -let updated = v2_rt::rc_map_insert(let_prov.clone(), bname, value_prov); +let value_prov = classify_body_provenance(&value, ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov); +let updated = v2_rt::rc_map_insert(let_prov.clone(), bname.clone(), value_prov.clone()); match let_body(expr.clone()) { - Some(body) => classify_body_provenance(&body, param_names.clone(), ¶m_types, &type_env, &func_sigs, updated), + Some(body) => classify_body_provenance(&body, ¶m_names, ¶m_types, &type_env, &func_sigs, &updated), None => v2_rt::rc_empty_map::>(), } }, - ExprData::ExprBlock => match expr.children.clone().last().cloned() { - Some(last_stmt) => classify_body_provenance(&last_stmt, param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone()), + ExprData::ExprBlock => { + let block_result = expr.children.clone().iter().cloned().fold(Rc::new(BlockProvFoldState { + prov: let_prov.clone(), + last: empty_prov_map(), +}), |state: Rc, stmt: Rc| match (*stmt.expr_data.clone()).clone() { + ExprData::ExprLet => { + let bname = let_binding_name_at(stmt.clone(), type_env.source_indices.clone()); +let value = let_value(stmt.clone()); +let value_prov = classify_body_provenance(&value, ¶m_names, ¶m_types, &type_env, &func_sigs, &state.prov.clone()); +let updated_prov = v2_rt::rc_map_insert(state.prov.clone(), bname.clone(), value_prov.clone()); +match let_body(stmt.clone()) { + Some(body) => Rc::new(BlockProvFoldState { + prov: state.prov.clone(), + last: classify_body_provenance(&body, ¶m_names, ¶m_types, &type_env, &func_sigs, &updated_prov), +}), + None => Rc::new(BlockProvFoldState { + prov: updated_prov.clone(), + last: value_prov.clone(), +}), +} +}, + _ => Rc::new(BlockProvFoldState { + prov: state.prov.clone(), + last: classify_body_provenance(&stmt, ¶m_names, ¶m_types, &type_env, &func_sigs, &state.prov.clone()), +}), +}); +block_result.last.clone() +}, + ExprData::ExprCast => classify_body_provenance(&cast_expr(expr.clone()), ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov), + ExprData::ExprReturn => match expr.children.clone().first().cloned() { + Some(inner) => classify_body_provenance(&inner, ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov), None => v2_rt::rc_empty_map::>(), }, _ => v2_rt::rc_empty_map::>(), @@ -4197,6 +4274,12 @@ match let_body(expr.clone()) { }) } +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct BlockProvFoldState { + pub prov: Rc>>>>, + pub last: Rc>>, +} + pub fn call_args_by_name(call: Rc, source_indices: Rc>>) -> Rc>> { call.children.clone().iter().cloned().fold(v2_rt::rc_empty_map::>(), |acc: Rc>>, arg_node: Rc| match arg_name_at(arg_node.clone(), source_indices.clone()) { Some(aname) => v2_rt::rc_map_insert(acc.clone(), aname.clone(), arg_value(&arg_node)), @@ -4236,7 +4319,7 @@ pub fn compose_callee_provenance(callee: String, arg_by_name: Rc Rc::new(v2_rt::map_keys(&callee_prov)).iter().cloned().fold(v2_rt::rc_empty_map::>(), |acc: Rc>>, callee_pname: String| match v2_rt::map_get(&callee_prov, callee_pname.clone()) { Some(callee_rel) => match v2_rt::map_get(&arg_by_name, callee_pname.clone()) { Some(arg) => { - let arg_prov = classify_body_provenance(&arg, param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone()); + let arg_prov = classify_body_provenance(&arg, ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov); Rc::new(v2_rt::map_keys(&arg_prov)).iter().cloned().fold(acc.clone(), |inner: Rc>>, pname: String| match v2_rt::map_get(&arg_prov, pname.clone()) { Some(arg_rel) => { let composed = compose_sub_value_relations(arg_rel.clone(), callee_rel.clone()); @@ -4272,12 +4355,12 @@ classify_terminal_per_field(&terminal, param_names, param_types, &type_env, func pub fn classify_terminal_per_field(expr: &Rc, param_names: Rc>, param_types: Rc>, type_env: &Rc, func_sigs: Rc>>, let_prov: Rc>>>>) -> Rc>>>> { match (*expr.expr_data.clone()).clone() { - ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in expr.children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), param_names.clone(), ¶m_types, &type_env, &func_sigs, let_prov.clone())); } __result }), + ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in expr.children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), ¶m_names, ¶m_types, &type_env, &func_sigs, &let_prov)); } __result }), ExprData::ExprMatch => { let arm_results = Rc::new({ let mut __result = Vec::new(); for arm in match_arm_nodes(expr.clone()).iter().cloned() { __result.push({ let arm_unwrapped = unwrap_body_terminal(&arm_body(&arm), let_prov.clone(), type_env.clone()); match (*arm_unwrapped.expr.clone().expr_data.clone()).clone() { - ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in arm_unwrapped.expr.clone().children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), param_names.clone(), ¶m_types, &type_env, &func_sigs, arm_unwrapped.let_prov.clone())); } __result }), + ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in arm_unwrapped.expr.clone().children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), ¶m_names, ¶m_types, &type_env, &func_sigs, &arm_unwrapped.let_prov.clone())); } __result }), _ => Rc::new(vec![]), } }); } __result }); @@ -4286,14 +4369,14 @@ meet_per_field_results(&arm_results) ExprData::ExprIf => { let then_unwrapped = unwrap_body_terminal(&if_then_branch(expr.clone()), let_prov.clone(), type_env.clone()); let then_result = match (*then_unwrapped.expr.clone().expr_data.clone()).clone() { - ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in then_unwrapped.expr.clone().children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), param_names.clone(), ¶m_types, &type_env, &func_sigs, then_unwrapped.let_prov.clone())); } __result }), + ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in then_unwrapped.expr.clone().children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), ¶m_names, ¶m_types, &type_env, &func_sigs, &then_unwrapped.let_prov.clone())); } __result }), _ => Rc::new(vec![]), }; match if_else_branch(expr.clone()) { Some(else_expr) => { let else_unwrapped = unwrap_body_terminal(&else_expr, let_prov.clone(), type_env.clone()); let else_result = match (*else_unwrapped.expr.clone().expr_data.clone()).clone() { - ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in else_unwrapped.expr.clone().children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), param_names.clone(), ¶m_types, &type_env, &func_sigs, else_unwrapped.let_prov.clone())); } __result }), + ExprData::ExprRecordLit { .. } => Rc::new({ let mut __result = Vec::new(); for fi in else_unwrapped.expr.clone().children.clone().iter().cloned() { __result.push(classify_body_provenance(&field_init_node_value(&fi), ¶m_names, ¶m_types, &type_env, &func_sigs, &else_unwrapped.let_prov.clone())); } __result }), _ => Rc::new(vec![]), }; meet_per_field_results(&Rc::new(vec![then_result, else_result]))