Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+167
View File
@@ -0,0 +1,167 @@
//! 为 result(call) 选择唯一且名义身份匹配的 choice。
//! 仅载荷形状相同不足以匹配业务错误;数值 Fault 不进入捕获集合。
use crate::compiler::{check::Context, model::*};
use crate::syntax::{ast, diagnostic::Diagnostic};
use std::collections::BTreeMap;
impl Context<'_> {
pub(crate) fn capture(
&mut self,
call: &ast::Expression,
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
let target = match expected {
Some(Type::Optional(inner)) => Some(inner.as_ref()),
expected => expected,
};
// 泛型解码直接使用结果 Choice 的成功字段作为类型上下文,不生成包装函数。
let decoded = if matches!(&call.kind, ast::ExpressionKind::Call(target, _)
if target.path().as_deref() == Some("dever.json.decode"))
{
let Some(Type::Named(index)) = target else {
return Err(self.error(
source,
"result(dever.json.decode(...)) requires an explicit result choice",
));
};
let Shape::Choice(variants) = &self.definitions[*index].shape else {
return Err(self.error(source, "JSON decode result requires a choice"));
};
let mut successes = variants.iter().filter(|variant| !variant.error);
let (Some(success), None) = (successes.next(), successes.next()) else {
return Err(self.error(
source,
"JSON decode result requires exactly one success variant",
));
};
let [(_, kind)] = success.fields.as_slice() else {
return Err(self.error(source, "JSON decode success variant requires one value"));
};
Some(kind.clone())
} else {
None
};
let value = self.expression(call, decoded.as_ref())?;
let decoded_failure;
let (origin, failures) = match &value.kind {
Node::Call(function, _) => {
let signature = &self.signatures[*function];
(signature.source, signature.failures.as_slice())
}
Node::Json(JsonOperation::Decode { error }, _) => {
decoded_failure = [Failure {
definition: *error,
variant: 1,
}];
(self.definitions[*error].source, decoded_failure.as_slice())
}
_ => {
return Err(self.error(
source,
"result requires a function call or typed JSON decode",
));
}
};
let mut candidates = Vec::new();
for (index, definition) in self.definitions.iter().enumerate() {
if definition.source != self.source && !self.definition_names.exported(index) {
continue;
}
if target.is_none()
&& definition.source != self.source
&& definition.source != origin
&& !failures.iter().any(|failure| failure.definition == index)
{
continue;
}
if target.is_some_and(|kind| *kind != Type::Named(index)) {
continue;
}
let Shape::Choice(variants) = &definition.shape else {
continue;
};
let successes = variants
.iter()
.enumerate()
.filter(|(_, variant)| !variant.error)
.collect::<Vec<_>>();
let [(success, variant)] = successes.as_slice() else {
continue;
};
let outputs_match = match &value.value_type {
Type::Unit => variant.fields.is_empty(),
Type::Outputs(outputs) => variant
.fields
.iter()
.map(|(_, kind)| kind)
.eq(outputs.iter().map(|(_, kind)| kind)),
kind => matches!(variant.fields.as_slice(), [(_, field)] if field == kind),
};
if !outputs_match {
continue;
}
if let Some(targets) = targets(index, variants, failures, self.definitions) {
candidates.push((index, *success, targets));
}
}
if candidates.len() != 1 {
return Err(Diagnostic::new(
"C005",
"result needs one unambiguous choice with matching success outputs and all declared errors",
source.span,
));
}
let (definition, success, targets) = candidates.pop().unwrap();
let capture = Capture {
call: value,
definition,
success,
targets,
};
self.coerce(
Expression {
kind: Node::Capture(Box::new(capture)),
value_type: Type::Named(definition),
span: source.span,
},
expected,
)
}
}
fn targets(
index: usize,
variants: &[Variant],
actual: &[Failure],
definitions: &[Definition],
) -> Option<Vec<(Failure, usize, bool)>> {
let mut mapping = BTreeMap::new();
for (position, variant) in variants.iter().enumerate().filter(|(_, value)| value.error) {
let direct = Failure {
definition: index,
variant: position,
};
if actual.contains(&direct) {
mapping.insert(direct, (position, false));
}
if let [(_, Type::Named(inner))] = variant.fields.as_slice()
&& let Shape::Choice(_) = &definitions[*inner].shape
{
for failure in actual.iter().filter(|failure| failure.definition == *inner) {
if mapping.insert(*failure, (position, true)).is_some() {
return None;
}
}
}
}
actual
.iter()
.map(|failure| {
mapping
.get(failure)
.map(|(variant, wrapped)| (*failure, *variant, *wrapped))
})
.collect()
}
@@ -0,0 +1,194 @@
//! 推导完整遍历、提前停止及归约中的失败处理义务。
//! 未知长度归约合并初始与迭代摘要直到稳定,保留空输入的可能性。
use super::{State, flow};
use crate::compiler::model::*;
use crate::syntax::diagnostic::Diagnostic;
use std::collections::{BTreeMap, BTreeSet};
impl State<'_> {
pub(super) fn collection(&mut self, collection: &Collection) -> Result<Flow, Diagnostic> {
if collection
.sequence
.value_type
.database_stream(self.definitions)
&& let Some(definition) = self.database_error
{
let Shape::Choice(variants) = &self.definitions[definition].shape else {
unreachable!()
};
self.failures
.extend((0..variants.len()).map(|variant| Failure {
definition,
variant,
}));
}
let sequence = self.expression(&collection.sequence)?;
if let Some(limit) = &collection.limit {
self.expression(limit)?;
}
let extra = collection
.extra
.as_ref()
.map(|value| self.expression(value))
.transpose()?;
let signature = &self.signatures[collection.handler];
self.failures.extend(&signature.failures);
let before = self.pending.clone();
// Stream 的失败元素尚未产生;只在实际消费合同中引入义务,关闭不等于丢弃已读错误。
let shape =
if let Type::Stream(item) | Type::AsyncStream(item) = &collection.sequence.value_type {
match &sequence.sequence {
// 转发流可能没有现存错误 token,但未来拉取仍会产生错误义务。
Some(Sequence::Unknown(value)) if value.tokens.is_empty() => {
Sequence::Unknown(Box::new(self.unknown(item, 0)?))
}
Some(sequence) => sequence.clone(),
None => Sequence::Unknown(Box::new(self.unknown(item, 0)?)),
}
} else {
sequence
.sequence
.clone()
.unwrap_or_else(|| Sequence::Unknown(Box::default()))
};
let mut produced = Vec::new();
let mut state = extra.clone().unwrap_or_default();
let mut fresh = BTreeMap::new();
match &shape {
Sequence::Known(items) => {
for item in items {
self.budget(0)?;
let value =
self.apply_handler(collection, item, &state, &mut BTreeMap::new())?;
if collection.operation.reduction() {
state = value;
produced.push(state.clone());
} else {
produced.push(value);
}
}
}
Sequence::Unknown(item) => {
if collection.operation.reduction() {
// 初始状态参与合并,因为未知集合可以完全不执行 handler。
loop {
self.budget(0)?;
let value = self.apply_handler(collection, item, &state, &mut fresh)?;
let previous = state.clone();
state.merge(&value);
if previous == state {
break;
}
}
produced.push(state.clone());
} else {
produced.push(self.apply_handler(collection, item, &state, &mut fresh)?);
}
}
}
let empty = matches!(&shape, Sequence::Known(items) if items.is_empty());
let nonempty = matches!(&shape, Sequence::Known(items) if !items.is_empty());
let partial = matches!(
collection.operation,
Operation::Find | Operation::ReduceUntil
);
self.pending = before;
// 提前结束只保证访问首项,不能用选中的输出消费剩余元素的义务。
if partial {
if let Sequence::Known(items) = &shape
&& let Some(first) = items.first()
{
self.pending.retain(|token| !first.tokens.contains(token));
}
} else {
self.pending
.retain(|token| !sequence.tokens.contains(token));
}
if let Some(extra) = &extra
&& (nonempty || collection.operation.reduction())
{
self.pending.retain(|token| !extra.tokens.contains(token));
}
let mut output = match collection.operation {
Operation::ParallelEach => Flow::default(),
Operation::Each if signature.outputs.is_empty() => Flow::default(),
Operation::Each => Flow::sequence(match shape {
Sequence::Known(_) => Sequence::Known(produced),
Sequence::Unknown(_) => Sequence::Unknown(Box::new(produced.pop().unwrap())),
}),
Operation::Filter if empty => Flow::sequence(Sequence::Known(Vec::new())),
Operation::Filter => Flow::sequence(Sequence::Unknown(Box::new(shape.element()))),
Operation::Find => {
let item = if empty {
return Ok(Flow::variants([(0, Flow::default())]));
} else if matches!(&shape, Sequence::Known(items) if items.len() == 1) {
shape.element()
} else {
self.unknown(&signature.inputs[0].1, 0)?
};
Flow::variants([(0, Flow::default()), (1, item)])
}
Operation::Sum => Flow::default(),
Operation::Reduce => state,
Operation::ReduceUntil => {
if let Some((first, rest)) = produced.split_first() {
let mut output = first.clone();
for value in rest {
output.merge(value);
}
output
} else {
state
}
}
};
let retained: BTreeSet<_> = self.pending.intersection(&output.tokens).copied().collect();
if !retained.is_empty() {
let mut mapping = BTreeMap::new();
for token in &output.tokens {
let target = if retained.contains(token) {
self.token()?
} else {
*token
};
mapping.insert(*token, BTreeSet::from([target]));
}
output = self.instantiate(&output, &mut mapping, 0)?;
}
self.pending.extend(&output.tokens);
self.closed_streams.extend(sequence.streams);
self.refresh_streams(&mut output);
Ok(output)
}
fn apply_handler(
&mut self,
collection: &Collection,
item: &Flow,
extra: &Flow,
fresh: &mut BTreeMap<usize, BTreeSet<usize>>,
) -> Result<Flow, Diagnostic> {
let signature = &self.signatures[collection.handler];
let mut mapping = fresh.clone();
for template in &signature.input_flows {
for token in &template.tokens {
mapping.remove(token);
}
}
for (template, actual) in signature.input_flows.iter().zip([item, extra]) {
flow::bind(template, actual, &mut mapping);
}
let output = if let Some(value) = &signature.returns {
self.instantiate(value, &mut mapping, 0)?
} else {
Flow::default()
};
*fresh = mapping;
if collection.operation == Operation::ReduceUntil {
Ok(output.fields.get(&0).cloned().unwrap_or_default())
} else {
Ok(output)
}
}
}
@@ -0,0 +1,371 @@
//! 按表达式求值规则传播失败摘要。
//! 可能被短路跳过的处理不能解除已有义务;捕获调用的参数失败仍向外传播。
use super::State;
use crate::compiler::model::*;
use crate::syntax::{ast::Literal, diagnostic::Diagnostic};
use std::collections::BTreeMap;
impl State<'_> {
pub(super) fn expression(&mut self, expression: &Expression) -> Result<Flow, Diagnostic> {
self.budget(0)?;
match &expression.kind {
Node::Enqueue(call, arguments) => {
for argument in arguments {
self.expression(argument)?;
}
let Shape::Choice(variants) = &self.definitions[call.error].shape else {
unreachable!()
};
self.failures
.extend((0..variants.len()).map(|variant| Failure {
definition: call.error,
variant,
}));
self.unknown(&expression.value_type, 0)
}
Node::Authorization(call, arguments) => {
for argument in arguments {
self.expression(argument)?;
}
for definition in [call.api_error, call.database_error] {
let Shape::Choice(variants) = &self.definitions[definition].shape else {
unreachable!()
};
self.failures
.extend((0..variants.len()).map(|variant| Failure {
definition,
variant,
}));
}
self.unknown(&expression.value_type, 0)
}
Node::Auth(call, arguments) => {
for argument in arguments {
self.expression(argument)?;
}
if call.operation != auth::Operation::Site {
let Shape::Choice(variants) = &self.definitions[call.error].shape else {
unreachable!()
};
self.failures.extend(variants.iter().enumerate().filter_map(
|(variant, value)| {
matches!(
value.name.as_str(),
"Invalid" | "Unauthorized" | "Forbidden"
)
.then_some(Failure {
definition: call.error,
variant,
})
},
));
}
self.unknown(&expression.value_type, 0)
}
Node::Json(JsonOperation::Decode { error }, arguments) => {
self.expression(&arguments[0])?;
self.failures.insert(Failure {
definition: *error,
variant: 1,
});
self.unknown(&expression.value_type, 0)
}
Node::Database(query, arguments) => {
for argument in arguments {
self.expression(argument)?;
}
let Shape::Choice(variants) = &self.definitions[query.error].shape else {
unreachable!()
};
self.failures
.extend((0..variants.len()).map(|variant| Failure {
definition: query.error,
variant,
}));
self.unknown(&expression.value_type, 0)
}
Node::Literal(Literal::Null) => Ok(Flow::variants([(0, Flow::default())])),
Node::Literal(_) => Ok(Flow::default()),
Node::Variable(index) => {
let mut value = self.values[*index].clone();
self.refresh_streams(&mut value);
Ok(value)
}
Node::Field(value, index) => Ok(self
.expression(value)?
.fields
.get(index)
.cloned()
.unwrap_or_default()),
Node::Some(value) => Ok(Flow::variants([(1, self.expression(value)?)])),
Node::List(values) => Ok(Flow::sequence(Sequence::Known(
values
.iter()
.map(|value| self.expression(value))
.collect::<Result<_, _>>()?,
))),
Node::Map(entries) => {
let mut values = Vec::new();
for (key, value) in entries {
values.push(Flow::fields([
(0, self.expression(key)?),
(1, self.expression(value)?),
]));
}
Ok(Flow::sequence(Sequence::Known(values)))
}
Node::Access(operation, arguments) => {
self.access(*operation, arguments, &expression.value_type)
}
Node::Net(operation, arguments) => {
for argument in arguments {
self.expression(argument)?;
}
match operation {
NetOperation::Connections => self.lazy_stream(),
NetOperation::Chunks => {
let Type::Named(index) = expression.value_type else {
unreachable!("checked stream result");
};
Ok(Flow::variants([
(0, Flow::fields([(0, self.lazy_stream()?)])),
(1, self.unknown_variant(index, 1, 0)?),
]))
}
_ => self.unknown(&expression.value_type, 0),
}
}
Node::Http(HttpOperation::Open | HttpOperation::OpenStream, arguments) => {
for argument in arguments {
let value = self.expression(argument)?;
// 上传源的失败由 HTTP 打开结果或响应流交付,不能留下重复义务。
self.pending.retain(|token| !value.tokens.contains(token));
}
let Type::Named(index) = expression.value_type else {
unreachable!("checked HTTP stream result");
};
// 下载尚未拉取;沿用 TCP 流的延迟失败证明,关闭不会伪造已读错误。
Ok(Flow::variants([
(
0,
Flow::fields([(0, Flow::fields([(2, self.lazy_stream()?)]))]),
),
(1, self.unknown_variant(index, 1, 0)?),
]))
}
Node::Crypto(_, arguments)
| Node::Assert(arguments)
| Node::Calendar(_, arguments)
| Node::Standard(_, arguments)
| Node::Bytes(_, arguments)
| Node::Http(_, arguments)
| Node::Tls(_, arguments)
| Node::Io(_, arguments)
| Node::Upload(_, arguments)
| Node::Json(_, arguments) => {
for argument in arguments {
self.expression(argument)?;
}
self.unknown(&expression.value_type, 0)
}
Node::Variant(index, variant, values) => {
let mut fields = Vec::new();
for (index, value) in values.iter().enumerate() {
fields.push((index, self.expression(value)?));
}
let value = Flow::fields(fields);
Ok(Flow::variants([(
*variant,
self.variant(*index, *variant, value)?,
)]))
}
Node::Record(_, values) => {
let mut fields = Vec::new();
for (index, value) in values {
fields.push((*index, self.expression(value)?));
}
Ok(Flow::fields(fields))
}
Node::Close(value) => {
let value = self.expression(value)?;
self.closed_streams.extend(value.streams);
Ok(Flow::default())
}
Node::Unary(_, value)
| Node::TestSecret(value)
| Node::TestClock(value)
| Node::TestDrain { limit: value, .. }
| Node::Decimal(value)
| Node::Id(value)
| Node::IdText(value)
| Node::Length(value)
| Node::Sum(value) => {
self.expression(value)?;
Ok(Flow::default())
}
Node::Binary(operator, left, right) => {
self.expression(left)?;
if matches!(operator.as_str(), "and" | "or") {
// 右侧仍需通过检查,但可能不执行,处理义务必须保留该路径。
if let Node::Literal(Literal::Bool(value)) = left.kind {
if value == (operator == "and") {
self.expression(right)?;
} else {
let pending = self.pending.clone();
let closed = self.closed_streams.clone();
self.expression(right)?;
self.pending = pending;
self.closed_streams = closed;
}
} else {
let pending = self.pending.clone();
self.expression(right)?;
self.pending.extend(pending);
}
} else {
self.expression(right)?;
}
Ok(Flow::default())
}
Node::Call(index, arguments) => {
let signature = &self.signatures[*index];
let mut mapping = self.arguments(arguments, signature)?;
self.failures.extend(&signature.failures);
if let Some(value) = &signature.returns {
self.instantiate(value, &mut mapping, 0)
} else {
Ok(Flow::default())
}
}
Node::Schedule(operation, index, arguments) => {
self.schedule(*operation, *index, arguments)
}
Node::Join { task, stop } => self.join(task, *stop),
Node::Timeout {
task,
millis,
fallback,
} => self.timeout(task, millis, *fallback),
Node::Race(tasks) => self.race(tasks),
Node::Group(limit) => self.group(limit),
Node::Stream(value) => {
let mut value = self.expression(value)?;
value.streams.insert(self.symbol()?);
Ok(value)
}
Node::Ticks(millis) => {
self.expression(millis)?;
Ok(Flow {
streams: [self.symbol()?].into(),
..Flow::default()
})
}
Node::Channel(capacity) => {
self.expression(capacity)?;
Ok(Flow::default())
}
Node::Send(channel, value) => {
self.expression(channel)?;
let value = self.expression(value)?;
self.pending.retain(|token| !value.tokens.contains(token));
Ok(Flow::default())
}
Node::Receive(channel) => {
self.expression(channel)?;
self.unknown(&expression.value_type, 0)
}
Node::GroupRun {
group,
target,
arguments,
} => self.group_run(group, *target, arguments),
Node::Timer(_, millis) => {
self.expression(millis)?;
self.unknown(&expression.value_type, 0)
}
Node::Capture(capture) => self.capture(capture),
Node::Collection(collection) => self.collection(collection),
}
}
fn capture(&mut self, capture: &Capture) -> Result<Flow, Diagnostic> {
let mut possibilities = BTreeMap::<usize, Flow>::new();
match &capture.call.kind {
Node::Call(function, arguments) => {
let signature = &self.signatures[*function];
let mut mapping = self.arguments(arguments, signature)?;
if let Some(value) = &signature.returns {
let value = self.instantiate(value, &mut mapping, 0)?;
let fields = if signature.outputs.len() == 1 {
Flow::fields([(0, value)])
} else {
value
};
possibilities.insert(capture.success, fields);
}
}
Node::Json(JsonOperation::Decode { .. }, arguments) => {
self.expression(&arguments[0])?;
let value = self.unknown(&capture.call.value_type, 0)?;
possibilities.insert(capture.success, Flow::fields([(0, value)]));
}
_ => unreachable!("checked capture call"),
}
for (failure, target, wrapped) in &capture.targets {
let source = self.unknown_variant(failure.definition, failure.variant, 0)?;
let value = if *wrapped {
self.variant(
capture.definition,
*target,
Flow::fields([(0, Flow::variants([(failure.variant, source)]))]),
)?
} else {
source
};
if let Some(previous) = possibilities.get_mut(target) {
previous.merge(&value);
} else {
possibilities.insert(*target, value);
}
}
Ok(Flow::variants(possibilities))
}
pub(super) fn fail(&mut self, value: &Expression) -> Result<(), Diagnostic> {
let flow = self.expression(value)?;
let Type::Named(index) = value.value_type else {
return Err(Diagnostic::new(
"C005",
"fail requires an error choice variant",
value.span,
));
};
let Shape::Choice(variants) = &self.definitions[index].shape else {
return Err(Diagnostic::new(
"C005",
"fail requires an error choice variant",
value.span,
));
};
let possible = if flow.variants.is_empty() {
(0..variants.len()).collect::<Vec<_>>()
} else {
flow.variants.keys().copied().collect()
};
for variant in possible {
if !variants[variant].error {
return Err(Diagnostic::new(
"C005",
"fail can receive a non-error variant",
value.span,
));
}
self.failures.insert(Failure {
definition: index,
variant,
});
}
Ok(())
}
}
+247
View File
@@ -0,0 +1,247 @@
//! 构造、绑定和实例化嵌套值的失败摘要。
//! 函数摘要中的义务编号在调用处重新映射,避免不同调用互相抵消错误。
use super::State;
use crate::compiler::model::*;
use crate::syntax::diagnostic::Diagnostic;
use std::collections::{BTreeMap, BTreeSet};
pub(super) fn tracked_types(definitions: &[Definition]) -> Vec<bool> {
fn tracked(kind: &Type, definitions: &[Definition], values: &mut [Option<bool>]) -> bool {
match kind {
Type::List(_) | Type::Map(_, _) | Type::AsyncStream(_) => true,
Type::Optional(inner) => tracked(inner, definitions, values),
Type::MapEntry(key, value) => {
tracked(key, definitions, values) || tracked(value, definitions, values)
}
Type::Named(index) => {
if let Some(value) = values[*index] {
return value;
}
let value = !definitions[*index].related
&& match &definitions[*index].shape {
Shape::Record(fields) => fields
.iter()
.any(|(_, kind)| tracked(kind, definitions, values)),
Shape::Choice(variants) => variants.iter().any(|variant| {
variant.error
|| variant
.fields
.iter()
.any(|(_, kind)| tracked(kind, definitions, values))
}),
};
values[*index] = Some(value);
value
}
_ => false,
}
}
// 类型检查已排除值类型环;只为含集合、流或错误的记录建立结构摘要。
let mut values = vec![None; definitions.len()];
for index in 0..definitions.len() {
tracked(&Type::Named(index), definitions, &mut values);
}
values.into_iter().map(Option::unwrap).collect()
}
impl State<'_> {
pub(super) fn unknown(&mut self, kind: &Type, depth: usize) -> Result<Flow, Diagnostic> {
self.budget(depth)?;
if let Type::List(inner) | Type::AsyncStream(inner) = kind {
let mut value =
Flow::sequence(Sequence::Unknown(Box::new(self.unknown(inner, depth + 1)?)));
if matches!(kind, Type::AsyncStream(_)) {
value.streams.insert(self.symbol()?);
}
return Ok(value);
}
if let Type::Map(key, value) = kind {
let entry = Type::MapEntry(key.clone(), value.clone());
return Ok(Flow::sequence(Sequence::Unknown(Box::new(
self.unknown(&entry, depth + 1)?,
))));
}
// 没有业务错误的记录仍可能含集合或流,字段摘要不能因此丢弃。
match kind {
Type::Named(index) if !self.tracked_types[*index] => Ok(Flow::default()),
Type::Named(index) => match &self.definitions[*index].shape {
Shape::Record(fields) => {
let mut values = Vec::new();
for (index, (_, kind)) in fields.iter().enumerate() {
values.push((index, self.unknown(kind, depth + 1)?));
}
Ok(Flow::fields(values))
}
Shape::Choice(variants) => {
let mut values = Vec::new();
for variant in 0..variants.len() {
values.push((variant, self.unknown_variant(*index, variant, depth + 1)?));
}
Ok(Flow::variants(values))
}
},
Type::Optional(inner) => Ok(Flow::variants([
(0, Flow::default()),
(1, self.unknown(inner, depth + 1)?),
])),
Type::MapEntry(key, value) => Ok(Flow::fields([
(0, self.unknown(key, depth + 1)?),
(1, self.unknown(value, depth + 1)?),
])),
_ => Ok(Flow::default()),
}
}
pub(super) fn unknown_variant(
&mut self,
index: usize,
variant: usize,
depth: usize,
) -> Result<Flow, Diagnostic> {
self.budget(depth)?;
let Shape::Choice(variants) = &self.definitions[index].shape else {
unreachable!()
};
let mut fields = Vec::new();
for (field, (_, kind)) in variants[variant].fields.iter().enumerate() {
fields.push((field, self.unknown(kind, depth + 1)?));
}
self.variant(index, variant, Flow::fields(fields))
}
pub(super) fn variant(
&mut self,
index: usize,
variant: usize,
mut fields: Flow,
) -> Result<Flow, Diagnostic> {
let Shape::Choice(variants) = &self.definitions[index].shape else {
unreachable!()
};
if variants[variant].error {
fields.tokens.insert(self.token()?);
fields.guaranteed = true;
}
Ok(fields)
}
pub(super) fn instantiate(
&mut self,
template: &Flow,
mapping: &mut BTreeMap<usize, BTreeSet<usize>>,
depth: usize,
) -> Result<Flow, Diagnostic> {
self.budget(depth)?;
// 子摘要按调用处重新映射,不先深拷贝再逐项覆盖。
let mut value = Flow {
guaranteed: template.guaranteed,
task: template.task.clone(),
..Flow::default()
};
for resource in &template.streams {
if !mapping.contains_key(resource) {
mapping.insert(*resource, BTreeSet::from([self.symbol()?]));
}
value.streams.extend(&mapping[resource]);
}
let mut tokens = BTreeSet::new();
for token in &template.tokens {
if !mapping.contains_key(token) {
mapping.insert(*token, BTreeSet::from([self.token()?]));
}
tokens.extend(&mapping[token]);
}
self.pending.extend(&tokens);
value.tokens = tokens;
for (index, field) in &template.fields {
value
.fields
.insert(*index, self.instantiate(field, mapping, depth + 1)?);
}
for (index, variant) in &template.variants {
value
.variants
.insert(*index, self.instantiate(variant, mapping, depth + 1)?);
}
value.sequence =
match &template.sequence {
Some(Sequence::Known(values)) => Some(Sequence::Known(
values
.iter()
.map(|value| self.instantiate(value, mapping, depth + 1))
.collect::<Result<_, _>>()?,
)),
Some(Sequence::Unknown(value)) => Some(Sequence::Unknown(Box::new(
self.instantiate(value, mapping, depth + 1)?,
))),
None => None,
};
self.refresh_streams(&mut value);
Ok(value)
}
}
pub(super) fn bind(template: &Flow, actual: &Flow, mapping: &mut BTreeMap<usize, BTreeSet<usize>>) {
for resource in &template.streams {
mapping
.entry(*resource)
.or_default()
.extend(&actual.streams);
}
for token in &template.tokens {
mapping.entry(*token).or_default();
}
let mut nested: BTreeSet<_> = template
.fields
.values()
.chain(template.variants.values())
.flat_map(|value| value.tokens.iter().copied())
.collect();
if let Some(sequence) = &template.sequence {
nested.extend(sequence.element().tokens);
}
for token in template.tokens.difference(&nested) {
mapping.entry(*token).or_default().extend(&actual.tokens);
}
for (index, field) in &template.fields {
bind(
field,
actual.fields.get(index).unwrap_or(&Flow::default()),
mapping,
);
}
for (index, variant) in &template.variants {
bind(
variant,
actual.variants.get(index).unwrap_or(&Flow::default()),
mapping,
);
}
if let Some(sequence) = &template.sequence {
let actual = actual.sequence.as_ref();
match (sequence, actual) {
(Sequence::Known(values), Some(Sequence::Known(actual)))
if values.len() == actual.len() =>
{
for (value, actual) in values.iter().zip(actual) {
bind(value, actual, mapping);
}
}
_ => bind(
&sequence.element(),
&actual.map(Sequence::element).unwrap_or_default(),
mapping,
),
}
}
}
pub(super) fn assign(target: &mut Flow, fields: &[usize], value: Flow) {
let Some((field, remaining)) = fields.split_first() else {
*target = value;
return;
};
assign(target.fields.entry(*field).or_default(), remaining, value);
*target = Flow::fields(std::mem::take(&mut target.fields));
}
+256
View File
@@ -0,0 +1,256 @@
//! 逐分句追踪业务失败的处理义务,并推导函数失败集合。
//! 只有返回、传播或显式恢复才能解除义务;分析结果不改变运行时错误身份。
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<usize>,
) -> Result<(), Diagnostic> {
let tracked_types = flow::tracked_types(definitions);
let mut next = 0;
let mut failures = BTreeSet::new();
let mut returns: Option<Flow> = None;
let mut inputs: Vec<Option<Flow>> = 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<usize>,
definitions: &'program [Definition],
signatures: &'program [Signature],
tracked_types: &'program [bool],
values: Vec<Flow>,
/// 尚未由当前分句处理或转移出去的错误义务。
pending: BTreeSet<usize>,
closed_streams: BTreeSet<usize>,
failures: BTreeSet<Failure>,
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::<Result<_, _>>()?;
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::<Result<Vec<_>, Diagnostic>>()?,
),
});
Ok(())
}
impl State<'_> {
fn symbol(&mut self) -> Result<usize, Diagnostic> {
self.budget(0)?;
let symbol = self.next;
self.next += 1;
Ok(symbol)
}
fn token(&mut self) -> Result<usize, Diagnostic> {
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<BTreeMap<usize, BTreeSet<usize>>, Diagnostic> {
self.call_inputs(arguments, signature, true)
}
fn call_inputs(
&mut self,
arguments: &[Expression],
signature: &Signature,
consume: bool,
) -> Result<BTreeMap<usize, BTreeSet<usize>>, Diagnostic> {
let mut mapping = BTreeMap::new();
let values = arguments
.iter()
.map(|argument| self.expression(argument))
.collect::<Result<Vec<_>, _>>()?;
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)
}
}
@@ -0,0 +1,82 @@
//! 追踪 List/Map 存取对失败义务的影响。
//! 读取、覆盖或删除部分元素不等于处理整个容器中的业务错误。
use super::State;
use crate::compiler::model::*;
use crate::syntax::diagnostic::Diagnostic;
impl State<'_> {
pub(super) fn access(
&mut self,
operation: Access,
arguments: &[Expression],
kind: &Type,
) -> Result<Flow, Diagnostic> {
let mut values = arguments
.iter()
.map(|argument| self.expression(argument))
.collect::<Result<Vec<_>, _>>()?;
let container = std::mem::take(&mut values[0]);
match operation {
Access::Contains => Ok(Flow::default()),
Access::Entries => Ok(container),
Access::Append => {
let value = std::mem::take(&mut values[1]);
let guaranteed = value.guaranteed || container.guaranteed;
let sequence = match container.sequence.expect("List flow") {
Sequence::Known(mut items) => {
items.push(value);
Sequence::Known(items)
}
Sequence::Unknown(mut item) => {
item.merge(&value);
Sequence::Unknown(item)
}
};
let mut output = Flow::sequence(sequence);
output.guaranteed = guaranteed;
Ok(output)
}
Access::First => {
if let Some(Sequence::Known(items)) = container.sequence {
if let Some(item) = items.into_iter().next() {
let Type::List(inner) = &arguments[0].value_type else {
unreachable!()
};
return Ok(if matches!(inner.as_ref(), Type::Optional(_)) {
item
} else {
Flow::variants([(1, item)])
});
}
Ok(Flow::variants([(0, Flow::default())]))
} else {
self.unknown(kind, 0)
}
}
Access::Get => self.unknown(kind, 0),
Access::Remove => {
if matches!(&container.sequence, Some(Sequence::Known(items)) if items.is_empty()) {
Ok(container)
} else {
self.unknown(kind, 0)
}
}
Access::Put => {
let entry = Flow::fields([
(0, std::mem::take(&mut values[1])),
(1, std::mem::take(&mut values[2])),
]);
if matches!(&container.sequence, Some(Sequence::Known(items)) if items.is_empty()) {
return Ok(Flow::sequence(Sequence::Known(vec![entry])));
}
let mut output = self.unknown(kind, 0)?;
let mut item = output.sequence.take().expect("Map flow").element();
item.merge(&entry);
output = Flow::sequence(Sequence::Unknown(Box::new(item)));
output.guaranteed = entry.guaranteed;
Ok(output)
}
}
}
}
+57
View File
@@ -0,0 +1,57 @@
//! 异步流别名只需在错误证明中跟踪:关闭的源不能再兑现处理原元素的承诺。
//! 这些编号和集合不会进入生成程序;普通 List 复制仍保留独立可消费的值。
use super::State;
use crate::compiler::model::{Flow, Sequence};
use crate::syntax::diagnostic::Diagnostic;
use std::collections::BTreeSet;
impl State<'_> {
pub(super) fn lazy_stream(&mut self) -> Result<Flow, Diagnostic> {
Ok(Flow {
streams: [self.symbol()?].into(),
..Flow::default()
})
}
pub(super) fn refresh_streams(&self, value: &mut Flow) -> bool {
if !value.streams.is_disjoint(&self.closed_streams) {
value.tokens.clear();
value.guaranteed = false;
value.sequence = Some(Sequence::Known(Vec::new()));
return true;
}
let mut nested = BTreeSet::new();
let mut changed = false;
for child in value.fields.values_mut().chain(value.variants.values_mut()) {
nested.extend(&child.tokens);
changed |= self.refresh_streams(child);
}
if let Some(sequence) = &mut value.sequence {
nested.extend(sequence.element().tokens);
match sequence {
Sequence::Known(items) => {
for item in items {
changed |= self.refresh_streams(item);
}
}
Sequence::Unknown(item) => changed |= self.refresh_streams(item),
}
}
if changed {
value.tokens.retain(|token| !nested.contains(token));
value.tokens.extend(
value
.fields
.values()
.chain(value.variants.values())
.flat_map(|child| child.tokens.iter().copied()),
);
if let Some(sequence) = &value.sequence {
value.tokens.extend(sequence.element().tokens);
}
value.guaranteed = false;
}
changed
}
}
+156
View File
@@ -0,0 +1,156 @@
//! 提交只保存处理承诺;正常 wait 才完成输入处理并引入输出错误义务。
//! stop 可能在工作开始前取消,不能把已存在的错误当成已经被子任务处理。
use super::State;
use crate::compiler::model::{failure::Deferred, *};
use crate::syntax::diagnostic::Diagnostic;
impl State<'_> {
pub(super) fn schedule(
&mut self,
operation: Schedule,
target: usize,
arguments: &[Expression],
) -> Result<Flow, Diagnostic> {
let signature = &self.signatures[target];
let mut mapping = self.call_inputs(arguments, signature, operation != Schedule::Run)?;
let before = self.pending.clone();
let inputs = mapping.values().flatten().copied().collect();
let output = if let Some(value) = &signature.returns {
self.instantiate(value, &mut mapping, 0)?
} else {
Flow::default()
};
if operation == Schedule::Run {
self.pending = before;
Ok(Flow {
task: Some(Box::new(Deferred {
inputs,
output,
failures: signature.failures.iter().copied().collect(),
})),
..Flow::default()
})
} else {
self.failures.extend(&signature.failures);
Ok(output)
}
}
pub(super) fn join(&mut self, task: &Expression, stop: bool) -> Result<Flow, Diagnostic> {
let value = self.expression(task)?;
let deferred = value.task.expect("checked affine handle");
self.failures.extend(&deferred.failures);
if !stop {
self.pending
.retain(|token| !deferred.inputs.contains(token));
}
self.pending.extend(&deferred.output.tokens);
Ok(deferred.output)
}
pub(super) fn timeout(
&mut self,
task: &Expression,
millis: &Expression,
fallback: usize,
) -> Result<Flow, Diagnostic> {
let flow = self.expression(task)?;
self.expression(millis)?;
let deferred = flow.task.expect("Task originates from run");
let Type::Task(target) = task.value_type else {
unreachable!("checked Task");
};
self.failures.extend(&self.signatures[target].failures);
let signature = &self.signatures[fallback];
self.failures.extend(&signature.failures);
let fallback = signature
.returns
.as_ref()
.map(|value| self.instantiate(value, &mut Default::default(), 0))
.transpose()?;
if fallback.is_none() {
self.pending
.retain(|token| !deferred.inputs.contains(token));
}
let mut output = self.signatures[target]
.returns
.as_ref()
.map(|_| deferred.output);
if let Some(fallback) = fallback {
if let Some(output) = &mut output {
output.merge(&fallback);
} else {
output = Some(fallback);
}
}
let output = output.unwrap_or_default();
self.pending.extend(&output.tokens);
Ok(output)
}
pub(super) fn race(&mut self, tasks: &[Expression]) -> Result<Flow, Diagnostic> {
let mut output: Option<Flow> = None;
let mut handled: Option<std::collections::BTreeSet<usize>> = None;
for task in tasks {
let value = self.expression(task)?;
let deferred = value.task.expect("Task originates from run");
let Type::Task(target) = task.value_type else {
unreachable!("checked Task");
};
self.failures.extend(&self.signatures[target].failures);
if self.signatures[target].returns.is_some() {
// 只有每个可能胜出的任务都处理的输入,才能在正常返回时解除义务。
if let Some(handled) = &mut handled {
handled.retain(|token| deferred.inputs.contains(token));
} else {
handled = Some(deferred.inputs);
}
if let Some(output) = &mut output {
output.merge(&deferred.output);
} else {
output = Some(deferred.output);
}
}
}
if let Some(handled) = handled {
self.pending.retain(|token| !handled.contains(token));
}
let output = output.unwrap_or_default();
self.pending.extend(&output.tokens);
Ok(output)
}
pub(super) fn group(&mut self, limit: &Expression) -> Result<Flow, Diagnostic> {
self.expression(limit)?;
Ok(Flow {
task: Some(Box::new(Deferred {
inputs: Default::default(),
output: Flow::default(),
failures: Default::default(),
})),
..Flow::default()
})
}
pub(super) fn group_run(
&mut self,
group: &Expression,
target: usize,
arguments: &[Expression],
) -> Result<Flow, Diagnostic> {
let Node::Variable(index) = group.kind else {
unreachable!("checked local Group");
};
let pending = self
.schedule(Schedule::Run, target, arguments)?
.task
.expect("checked run");
let group = self.values[index].task.as_mut().expect("checked Group");
group.inputs.extend(pending.inputs);
group.failures.extend(pending.failures);
// 提交时可能观察到已有子任务的失败;正常 wait 才兑现所有输入处理承诺。
self.failures.extend(&group.failures);
Ok(Flow::default())
}
}