//! 逐分句追踪业务失败的处理义务,并推导函数失败集合。 //! 只有返回、传播或显式恢复才能解除义务;分析结果不改变运行时错误身份。 mod capture; mod collection; mod expression; mod flow; mod storage; mod stream; mod task; use crate::compiler::model::*; use crate::syntax::{diagnostic::Diagnostic, source::Span}; use std::collections::{BTreeMap, BTreeSet}; pub(super) fn check( function: &mut Function, definitions: &[Definition], signatures: &[Signature], database_error: Option, ) -> Result<(), Diagnostic> { let tracked_types = flow::tracked_types(definitions); let mut next = 0; let mut failures = BTreeSet::new(); let mut returns: Option = None; let mut inputs: Vec> = vec![None; function.signature.inputs.len()]; for clause in &function.clauses { let mut state = State { database_error, definitions, signatures, tracked_types: &tracked_types, values: vec![Flow::default(); clause.variables.len()], pending: BTreeSet::new(), closed_streams: BTreeSet::new(), failures: BTreeSet::new(), next, work: 0, span: clause.span, }; for (position, (_, kind)) in function.signature.inputs.iter().enumerate() { let pattern = &clause.patterns[position]; let value = match (&clause.variables[position].1, pattern) { (_, Pattern::Null) => Flow::default(), (Type::Named(index), _) if matches!(definitions[*index].shape, Shape::Choice(_)) => { let Shape::Choice(variants) = &definitions[*index].shape else { unreachable!() }; let mut possibilities = Vec::new(); for variant in 0..variants.len() { if super::clauses::accepts_variant( pattern, variant, function .clauses .iter() .map(|clause| clause.patterns[position].clone()), ) { possibilities .push((variant, state.unknown_variant(*index, variant, 0)?)); } } Flow::variants(possibilities) } (kind, _) => state.unknown(kind, 0)?, }; let input = if matches!(kind, Type::Optional(_)) && matches!(pattern, Pattern::Present(_) | Pattern::Other) { Flow::variants([(1, value.clone())]) } else if matches!(pattern, Pattern::Null) { Flow::variants([(0, Flow::default())]) } else { value.clone() }; if let Some(previous) = &mut inputs[position] { previous.merge(&input); } else { inputs[position] = Some(input); } state.values[position] = value; } for binding in &clause.bindings { let Pattern::Variant(variant) = clause.patterns[binding.input].inner() else { unreachable!() }; state.values[binding.variable] = state.values[binding.input] .variants .get(variant) .and_then(|value| value.fields.get(&binding.field)) .cloned() .unwrap_or_default(); } for statement in &clause.body { match statement { Statement::Assign { variable, fields, value, } => { let value = state.expression(value)?; flow::assign(&mut state.values[*variable], fields, value); } Statement::Action(value) => { state.expression(value)?; } Statement::Fail(value) => state.fail(value)?, } } if !clause.terminates { let mut output = match clause.outputs.as_slice() { [] => Flow::default(), [index] => state.values[*index].clone(), outputs => Flow::fields( outputs .iter() .enumerate() .map(|(field, index)| (field, state.values[*index].clone())), ), }; state.refresh_streams(&mut output); if function.signature.recovery.is_none() && !output.guaranteed && !state.pending.is_subset(&output.tokens) { return Err(Diagnostic::new( "C012", "failure is not handled: return, propagate, or declare an explicit recovery policy", clause.span, )); } if let Some(previous) = &mut returns { previous.merge(&output); } else { returns = Some(output); } } next = state.next; failures.extend(state.failures); } function.signature.failures = failures.into_iter().collect(); function.signature.input_flows = inputs.into_iter().map(Option::unwrap).collect(); function.signature.returns = returns; Ok(()) } struct State<'program> { database_error: Option, definitions: &'program [Definition], signatures: &'program [Signature], tracked_types: &'program [bool], values: Vec, /// 尚未由当前分句处理或转移出去的错误义务。 pending: BTreeSet, closed_streams: BTreeSet, failures: BTreeSet, next: usize, work: usize, span: Span, } pub(super) fn prototype( signature: &mut Signature, definitions: &[Definition], span: Span, ) -> Result<(), Diagnostic> { let tracked_types = flow::tracked_types(definitions); let mut state = State { database_error: None, definitions, signatures: &[], tracked_types: &tracked_types, values: Vec::new(), pending: BTreeSet::new(), closed_streams: BTreeSet::new(), failures: BTreeSet::new(), next: 0, work: 0, span, }; signature.input_flows = signature .inputs .iter() .map(|(_, kind)| state.unknown(kind, 0)) .collect::>()?; signature.returns = Some(match signature.outputs.as_slice() { [] => Flow::default(), [(_, kind)] => state.unknown(kind, 0)?, fields => Flow::fields( fields .iter() .enumerate() .map(|(index, (_, kind))| Ok((index, state.unknown(kind, 0)?))) .collect::, Diagnostic>>()?, ), }); Ok(()) } impl State<'_> { fn symbol(&mut self) -> Result { self.budget(0)?; let symbol = self.next; self.next += 1; Ok(symbol) } fn token(&mut self) -> Result { let token = self.symbol()?; self.pending.insert(token); Ok(token) } fn budget(&mut self, depth: usize) -> Result<(), Diagnostic> { // 达到分析边界就报告失败,不能把未完成的证明当作合同通过。 self.work += 1; if self.work > 16_384 || depth > 128 { return Err(Diagnostic::new( "C015", "failure analysis exceeds its structural limit", self.span, )); } Ok(()) } fn arguments( &mut self, arguments: &[Expression], signature: &Signature, ) -> Result>, Diagnostic> { self.call_inputs(arguments, signature, true) } fn call_inputs( &mut self, arguments: &[Expression], signature: &Signature, consume: bool, ) -> Result>, Diagnostic> { let mut mapping = BTreeMap::new(); let values = arguments .iter() .map(|argument| self.expression(argument)) .collect::, _>>()?; for (value, template) in values.iter().zip(&signature.input_flows) { flow::bind(template, value, &mut mapping); if consume { self.pending.retain(|token| !value.tokens.contains(token)); } } Ok(mapping) } }