Phase 12 TypeBox統合ABI設計完了: C ABI + Nyash ABI革命的統合
主な成果: - TypeBox(型情報をBoxとして扱う)による統合ABI設計 - C ABI + Nyash ABIの完全統合仕様書作成 - 3大AI専門家(Gemini/Codex/ChatGPT5)による検証済み - ChatGPT5の10個の安全性改善提案を反映 - README.mdのドキュメント更新(全起点から到達可能) MapBox拡張: - string型キーサポート(従来のi64に加えて) - remove/clear/getOr/keysStr/valuesStr/toJson実装 - keys()/values()のランタイムシムサポート(TypeBox待ち) その他の改善: - Phase 11.9(文法統一化)ドキュメント追加 - Phase 16(FoldLang)ドキュメント追加 - 非同期タイムアウトテスト追加 - 各種ビルド警告(未使用import等)は次のリファクタリングで対応予定 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1225,15 +1225,34 @@ impl MirBuilder {
|
||||
self.current_static_box = Some(box_name.clone());
|
||||
// Look for the main() method
|
||||
let out = if let Some(main_method) = methods.get("main") {
|
||||
if let ASTNode::FunctionDeclaration { body, .. } = main_method {
|
||||
if let ASTNode::FunctionDeclaration { params, body, .. } = main_method {
|
||||
// Convert the method body to a Program AST node and lower it
|
||||
let program_ast = ASTNode::Program {
|
||||
statements: body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
|
||||
// Bind default parameters if present (e.g., args=[])
|
||||
// Save current var map; inject defaults; restore after lowering
|
||||
let saved_var_map = std::mem::take(&mut self.variable_map);
|
||||
// Prepare defaults for known patterns
|
||||
for p in params.iter() {
|
||||
let pid = self.value_gen.next();
|
||||
// Heuristic: for parameter named "args", create new ArrayBox(); else use Void
|
||||
if p == "args" {
|
||||
// new ArrayBox() -> pid
|
||||
// Emit NewBox for ArrayBox with no args
|
||||
self.emit_instruction(MirInstruction::NewBox { dst: pid, box_type: "ArrayBox".to_string(), args: vec![] })?;
|
||||
} else {
|
||||
self.emit_instruction(MirInstruction::Const { dst: pid, value: ConstValue::Void })?;
|
||||
}
|
||||
self.variable_map.insert(p.clone(), pid);
|
||||
}
|
||||
// Use existing Program lowering logic
|
||||
self.build_expression(program_ast)
|
||||
let lowered = self.build_expression(program_ast);
|
||||
// Restore variable map
|
||||
self.variable_map = saved_var_map;
|
||||
lowered
|
||||
} else {
|
||||
Err("main method in static box is not a FunctionDeclaration".to_string())
|
||||
}
|
||||
@ -1526,6 +1545,65 @@ impl MirBuilder {
|
||||
return Ok(dst);
|
||||
}
|
||||
}
|
||||
// ExternCall: env.X.* pattern via field access (e.g., env.future.delay)
|
||||
if let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object.clone() {
|
||||
if let ASTNode::Variable { name: env_name, .. } = *env_obj {
|
||||
if env_name == "env" {
|
||||
// Build args first (extern uses evaluated args only)
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in &arguments { arg_values.push(self.build_expression(arg.clone())?); }
|
||||
match (env_field.as_str(), method.as_str()) {
|
||||
("future", "delay") => {
|
||||
let result_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(result_id),
|
||||
iface_name: "env.future".to_string(),
|
||||
method_name: "delay".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::READ.add(Effect::Io),
|
||||
})?;
|
||||
return Ok(result_id);
|
||||
}
|
||||
("task", "currentToken") => {
|
||||
let result_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(result_id),
|
||||
iface_name: "env.task".to_string(),
|
||||
method_name: "currentToken".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::READ,
|
||||
})?;
|
||||
return Ok(result_id);
|
||||
}
|
||||
("task", "cancelCurrent") => {
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.task".to_string(),
|
||||
method_name: "cancelCurrent".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO,
|
||||
})?;
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
|
||||
return Ok(void_id);
|
||||
}
|
||||
("console", "log") => {
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.console".to_string(),
|
||||
method_name: "log".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO,
|
||||
})?;
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
|
||||
return Ok(void_id);
|
||||
}
|
||||
_ => { /* fallthrough to normal method lowering */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ExternCall判定はobjectの変数解決より先に行う(未定義変数で落とさない)
|
||||
if let ASTNode::Variable { name: object_name, .. } = object.clone() {
|
||||
// Build argument expressions first (externはobject自体を使わない)
|
||||
|
||||
@ -583,13 +583,14 @@ impl MirBuilder {
|
||||
pub(super) fn build_await_expression(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
// Evaluate the expression (should be a Future)
|
||||
let future_value = self.build_expression(expression)?;
|
||||
|
||||
// Insert checkpoint before await (safepoint)
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
// Create result value for the await
|
||||
let result_id = self.value_gen.next();
|
||||
|
||||
// Emit await instruction
|
||||
self.emit_instruction(MirInstruction::Await { dst: result_id, future: future_value })?;
|
||||
|
||||
// Insert checkpoint after await (safepoint)
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
Ok(result_id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +258,39 @@ mod tests {
|
||||
let dump = MirPrinter::new().print_module(&result.module);
|
||||
assert!(dump.contains("await"), "Expected await in MIR dump. Got:\n{}", dump);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_await_has_checkpoints() {
|
||||
use crate::ast::{LiteralValue, Span};
|
||||
// Build: await 1
|
||||
let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), span: Span::unknown() };
|
||||
let mut compiler = MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
// Verifier should pass (await flanked by safepoints)
|
||||
assert!(result.verification_result.is_ok(), "Verifier failed for await checkpoints: {:?}", result.verification_result);
|
||||
let dump = compiler.dump_mir(&result.module);
|
||||
// Expect at least two safepoints in the function (before/after await)
|
||||
let sp_count = dump.matches("safepoint").count();
|
||||
assert!(sp_count >= 2, "Expected >=2 safepoints around await, got {}. Dump:\n{}", sp_count, dump);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rewritten_await_still_checkpoints() {
|
||||
use crate::ast::{LiteralValue, Span};
|
||||
// Enable rewrite so Await → ExternCall(env.future.await)
|
||||
std::env::set_var("NYASH_REWRITE_FUTURE", "1");
|
||||
let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), span: Span::unknown() };
|
||||
let mut compiler = MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
// Verifier should still pass (checkpoint verification includes ExternCall await)
|
||||
assert!(result.verification_result.is_ok(), "Verifier failed for rewritten await checkpoints: {:?}", result.verification_result);
|
||||
let dump = compiler.dump_mir(&result.module);
|
||||
assert!(dump.contains("env.future.await"), "Expected rewritten await extern call. Dump:\n{}", dump);
|
||||
let sp_count = dump.matches("safepoint").count();
|
||||
assert!(sp_count >= 2, "Expected >=2 safepoints around rewritten await, got {}. Dump:\n{}", sp_count, dump);
|
||||
// Cleanup env
|
||||
std::env::remove_var("NYASH_REWRITE_FUTURE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_throw_compilation() {
|
||||
|
||||
@ -256,7 +256,8 @@ impl MirVerifier {
|
||||
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
||||
}
|
||||
|
||||
/// Ensure that each Await instruction is immediately preceded and followed by a checkpoint
|
||||
/// Ensure that each Await instruction (or ExternCall(env.future.await)) is immediately
|
||||
/// preceded and followed by a checkpoint.
|
||||
/// A checkpoint is either MirInstruction::Safepoint or ExternCall("env.runtime", "checkpoint").
|
||||
fn verify_await_checkpoints(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||
use super::MirInstruction as I;
|
||||
@ -269,7 +270,12 @@ impl MirVerifier {
|
||||
for (bid, block) in &function.blocks {
|
||||
let instrs = &block.instructions;
|
||||
for (idx, inst) in instrs.iter().enumerate() {
|
||||
if let I::Await { .. } = inst {
|
||||
let is_await_like = match inst {
|
||||
I::Await { .. } => true,
|
||||
I::ExternCall { iface_name, method_name, .. } => iface_name == "env.future" && method_name == "await",
|
||||
_ => false,
|
||||
};
|
||||
if is_await_like {
|
||||
// Check immediate previous
|
||||
if idx == 0 || !is_cp(&instrs[idx - 1]) {
|
||||
errors.push(VerificationError::MissingCheckpointAroundAwait { block: *bid, instruction_index: idx, position: "before" });
|
||||
|
||||
Reference in New Issue
Block a user