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

+284
View File
@@ -0,0 +1,284 @@
//! 校验任务提交、受限 worker 边界及等待的具体结果类型。
mod liveness;
mod ownership;
use super::Context;
use crate::compiler::model::*;
use crate::syntax::{ast, diagnostic::Diagnostic};
pub(super) use ownership::check;
impl Context<'_> {
pub(super) fn schedule(
&mut self,
operation: Schedule,
arguments: &[ast::Expression],
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
if operation == Schedule::Run && arguments.len() == 2 {
return self.group_run(arguments, expected, source);
}
let [call] = arguments else {
return Err(self.error(source, "task boundary requires one function call"));
};
let value = self.expression(call, None)?;
let Node::Call(function, values) = value.kind else {
return Err(self.error(source, "task boundary requires an ordinary function call"));
};
let signature = &self.signatures[function];
if operation != Schedule::Run
&& (signature.effects.suspend
|| signature.effects.handler
|| signature.effects.schedule
|| (operation == Schedule::Parallel && !signature.pure)
|| (operation == Schedule::Blocking && !signature.effects.blocking))
{
return Err(Diagnostic::new(
"C013",
"worker boundary requires a pure or known blocking synchronous function",
source.span,
));
}
if !transferable(signature, self.definitions) {
return Err(Diagnostic::new(
"C013",
"task inputs, outputs and failure payloads must be transferable",
source.span,
));
}
self.coerce(
Expression {
value_type: if operation == Schedule::Run {
Type::Task(function)
} else {
signature.output()
},
kind: Node::Schedule(operation, function, values),
span: source.span,
},
expected,
)
}
pub(super) fn join(
&mut self,
stop: bool,
arguments: &[ast::Expression],
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
let [argument] = arguments else {
return Err(self.error(source, "wait/stop requires one Task"));
};
let task = self.expression(argument, None)?;
let output = match task.value_type {
Type::Task(function) => self.signatures[function].output(),
Type::Group(_) => Type::Unit,
_ => return Err(self.error(source, "wait/stop requires a Task or Group")),
};
if stop && output != Type::Unit {
return Err(self.error(source, "stop requires a zero-output Task"));
}
self.coerce(
Expression {
value_type: output,
kind: Node::Join {
task: Box::new(task),
stop,
},
span: source.span,
},
expected,
)
}
pub(super) fn group(
&mut self,
arguments: &[ast::Expression],
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
let [limit] = arguments else {
return Err(self.error(source, "group requires one limit"));
};
let limit = self.expression(limit, Some(&Type::Int))?;
self.coerce(
Expression {
value_type: Type::Group(self.function),
kind: Node::Group(Box::new(limit)),
span: source.span,
},
expected,
)
}
fn group_run(
&mut self,
arguments: &[ast::Expression],
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
let group = self.expression(&arguments[0], None)?;
if !matches!(group.value_type, Type::Group(_)) || !matches!(group.kind, Node::Variable(_)) {
return Err(self.error(source, "run(group, call) requires a local Group"));
}
let scheduled = self.schedule(Schedule::Run, &arguments[1..], None, source)?;
let Node::Schedule(Schedule::Run, target, values) = scheduled.kind else {
unreachable!("checked run");
};
if !self.signatures[target].outputs.is_empty() {
return Err(self.error(source, "Group tasks must have zero outputs"));
}
self.coerce(
Expression {
value_type: Type::Unit,
kind: Node::GroupRun {
group: Box::new(group),
target,
arguments: values,
},
span: source.span,
},
expected,
)
}
pub(super) fn timeout(
&mut self,
arguments: &[ast::Expression],
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
let [task, millis, fallback] = arguments else {
return Err(self.error(
source,
"timeout requires a Task, duration and static fallback",
));
};
let task = self.expression(task, None)?;
let Type::Task(target) = task.value_type else {
return Err(self.error(source, "timeout requires a Task"));
};
let millis = self.expression(millis, Some(&Type::Int))?;
let fallback = self
.scope
.targets
.get(&fallback.span.start)
.copied()
.ok_or_else(|| {
self.error(
source,
"timeout fallback must be a zero-input static function",
)
})?;
if !self.signatures[fallback].inputs.is_empty()
|| self.signatures[fallback].outputs != self.signatures[target].outputs
{
return Err(self.error(
source,
"timeout fallback must have the Task's named output signature",
));
}
self.coerce(
Expression {
value_type: self.signatures[target].output(),
kind: Node::Timeout {
task: Box::new(task),
millis: Box::new(millis),
fallback,
},
span: source.span,
},
expected,
)
}
pub(super) fn race(
&mut self,
arguments: &[ast::Expression],
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
if arguments.len() < 2 {
return Err(self.error(source, "race requires at least two Tasks"));
}
let mut tasks = Vec::new();
let mut signature = None;
for argument in arguments {
let task = self.expression(argument, None)?;
let Type::Task(target) = task.value_type else {
return Err(self.error(argument, "race arguments must be Tasks"));
};
let outputs = &self.signatures[target].outputs;
if signature.is_some_and(|previous| previous != outputs) {
return Err(self.error(argument, "racing Tasks must have identical named outputs"));
}
signature = Some(outputs);
tasks.push(task);
}
let Type::Task(first) = tasks[0].value_type else {
unreachable!("checked Task");
};
self.coerce(
Expression {
value_type: self.signatures[first].output(),
kind: Node::Race(tasks),
span: source.span,
},
expected,
)
}
pub(super) fn timer(
&mut self,
timer: Timer,
arguments: &[ast::Expression],
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
let package = if timer == Timer::Delay {
"dever.task"
} else {
"dever.time"
};
if self.definition_names.packages[self.source] != package {
return Err(Diagnostic::new(
"C006",
"timer intrinsics belong to their official package",
source.span,
));
}
let [millis] = arguments else {
return Err(self.error(source, "sleep requires one duration"));
};
let value = self.expression(millis, Some(&Type::Int))?;
let value_type = self.io_result("dever.time.SleepResult", &[("Done", vec![])], source)?;
self.coerce(
Expression {
value_type,
kind: Node::Timer(timer, Box::new(value)),
span: source.span,
},
expected,
)
}
}
pub(super) fn transferable(signature: &Signature, definitions: &[Definition]) -> bool {
// 入队继承入口提供的只读执行身份;请求、Cookie 和可变认证上下文仍不能离开请求。
!signature.effects.request
&& signature
.inputs
.iter()
.chain(&signature.outputs)
.all(|(_, kind)| kind.transferable(definitions))
&& signature.failures.iter().all(|failure| {
let Shape::Choice(variants) = &definitions[failure.definition].shape else {
unreachable!()
};
variants[failure.variant]
.fields
.iter()
.all(|(_, kind)| kind.transferable(definitions))
})
}