Files
hakorune/src/mir/builder/calls/build.rs
tomoaki 46fbe12ce6 feat(repl): Phase 288.1 session persistence + auto-display
実装内容:
- AST Rewriter (~430行): 未宣言変数を __repl.get/set に変換
- ExternCall Bridge: VM で __repl.get/set ハンドラー実装
- Rc<RefCell<>> セッション共有: VM と REPL runner 間で永続化
- 式自動表示: pure expression の結果を自動出力
- _ 変数: 最後の表示値を保存(Void は除外)
- .reset 実装: セッション変数の完全クリア
- Fail-Fast: 未定義変数読み取りで明示的エラー + ヒント

変更ファイル (8ファイル, +592行):
- src/runner/repl/ast_rewriter.rs (NEW, +430行)
- src/runner/repl/repl_runner.rs (+84/-35行)
- src/backend/mir_interpreter/handlers/externals.rs (+54行)
- src/mir/builder/calls/build.rs (+41行)
- src/backend/mir_interpreter/mod.rs (+12行)
- src/runner/repl/repl_session.rs (+11/-9行)
- src/runner/repl/mod.rs (+2行)
- src/runner/mod.rs (+2/-1行)

REPL専用設計(src/mir/builder/calls/build.rs の特別扱い理由):
- __repl.get/set は REPL mode 専用の橋渡し機能
- try_build_repl_method_call() で早期検出・ExternCall 変換
- file mode では決して使用されない(VM で "outside REPL mode" エラー)
- 将来的にも file mode への影響ゼロを保証

検証済み:
- 変数永続化: x = 42; print(x) → 42 
- 式自動表示: 1 + 1 → 2 
- _ 変数: 10 * 2 → 20; _ → 20 
- Fail-Fast: 未定義エラー + ヒント 
- 回帰テスト: 154/154 PASS 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 23:04:59 +09:00

756 lines
28 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 🎯 箱理論: Call構築専用モジュール
//!
//! 責務: ASTからCall構築のみ
//! - build_function_call: 関数呼び出し構築
//! - build_method_call: メソッド呼び出し構築
//! - build_from_expression: from式構築
use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, MirType, ValueId};
use super::special_handlers;
use super::CallTarget;
use crate::ast::{ASTNode, LiteralValue};
use crate::mir::TypeOpKind;
impl MirBuilder {
// Build function call: name(args)
pub fn build_function_call(
&mut self,
name: String,
args: Vec<ASTNode>,
) -> Result<ValueId, String> {
// Dev trace
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
let cur_fun = self
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_else(|| "<none>".to_string());
eprintln!(
"[builder] function-call name={} static_ctx={} in_fn={}",
name,
self.comp_ctx.current_static_box.as_deref().unwrap_or(""),
cur_fun
);
}
// 0. Phase 285W-Syntax-0.1: Reject weak(...) function call syntax
// SSOT: docs/reference/language/lifecycle.md - weak <expr> is the ONLY valid syntax
if name == "weak" {
eprintln!("[Phase285W-0.1] Rejecting weak(...) function call");
return Err(format!(
"Invalid syntax: weak(...). Use unary operator: weak <expr>\n\
Help: Change 'weak(obj)' to 'weak obj' (unary operator, no parentheses)\n\
SSOT: docs/reference/language/lifecycle.md"
));
}
// 1. TypeOp wiring: isType(value, "Type"), asType(value, "Type")
if let Some(result) = self.try_build_typeop_function(&name, &args)? {
return Ok(result);
}
// 2. Math function handling
let raw_args = args.clone();
if let Some(res) = self.try_handle_math_function(&name, raw_args) {
return res;
}
// 3. Build argument values
let arg_values = self.build_call_args(&args)?;
// 4. Special-case: global str(x) → x.str() normalization
if name == "str" && arg_values.len() == 1 {
return self.build_str_normalization(arg_values[0]);
}
// 5. Determine call route (unified vs legacy)
let use_unified = super::call_unified::is_unified_call_enabled()
&& (super::super::call_resolution::is_builtin_function(&name)
|| super::super::call_resolution::is_extern_function(&name));
if !use_unified {
self.build_legacy_function_call(name, arg_values)
} else {
self.build_unified_function_call(name, arg_values)
}
}
// Build method call: object.method(arguments)
pub fn build_method_call(
&mut self,
object: ASTNode,
method: String,
arguments: Vec<ASTNode>,
) -> Result<ValueId, String> {
// Debug: Check recursion depth
const MAX_METHOD_DEPTH: usize = 100;
self.recursion_depth += 1;
if self.recursion_depth > MAX_METHOD_DEPTH {
eprintln!(
"[FATAL] build_method_call recursion depth exceeded {}",
MAX_METHOD_DEPTH
);
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
eprintln!("[FATAL] Method: {}", method);
return Err(format!(
"build_method_call recursion depth exceeded: {}",
self.recursion_depth
));
}
let result = self.build_method_call_impl(object, method, arguments);
self.recursion_depth -= 1;
result
}
fn build_method_call_impl(
&mut self,
object: ASTNode,
method: String,
arguments: Vec<ASTNode>,
) -> Result<ValueId, String> {
self.trace_method_call_if_enabled(&object, &method);
// 0. Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog へ直接 lowering
if let Some(result) = self.try_build_mir_debug_method_call(&object, &method, &arguments)? {
return Ok(result);
}
// Phase 288.1: REPL session variable bridge: __repl.get/set → ExternCall
if let Some(result) = self.try_build_repl_method_call(&object, &method, &arguments)? {
return Ok(result);
}
// 1. Static box method call: BoxName.method(args)
if let Some(result) = self.try_build_static_receiver_method_call(&object, &method, &arguments)? {
return Ok(result);
}
// 2. Handle env.* methods
if let Some(res) = self.try_handle_env_method(&object, &method, &arguments) {
return res;
}
// 3. Phase 269 P1.2: ReceiverNormalizeBox - MethodCall 共通入口 SSOT
if let Some(result) = self.try_normalize_this_me_method_call(&object, &method, &arguments)? {
return Ok(result);
}
// 4. Build object value
let object_value = self.build_expression(object.clone())?;
// Phase 287 P4: Debug object value after build_expression
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
eprintln!("[P287-DEBUG] After build_expression: object_value={:?}", object_value);
}
// Debug trace for receiver
self.trace_receiver_if_enabled(&object, object_value);
// 5. Handle TypeOp methods: value.is("Type") / value.as("Type")
if let Some(type_name) = special_handlers::is_typeop_method(&method, &arguments) {
return self.handle_typeop_method(object_value, &method, &type_name);
}
// 6. Fallback: standard Box/Plugin method call
self.handle_standard_method_call(object_value, method, &arguments)
}
fn trace_method_call_if_enabled(&self, object: &ASTNode, method: &str) {
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() != Some("1") {
return;
}
let kind = match object {
ASTNode::Variable { .. } => "Variable",
ASTNode::FieldAccess { .. } => "FieldAccess",
ASTNode::This { .. } => "This",
ASTNode::Me { .. } => "Me",
_ => "Other",
};
eprintln!("[builder] method-call object kind={} method={}", kind, method);
}
fn try_build_mir_debug_method_call(
&mut self,
object: &ASTNode,
method: &str,
arguments: &[ASTNode],
) -> Result<Option<ValueId>, String> {
let ASTNode::Variable { name: obj_name, .. } = object else {
return Ok(None);
};
if obj_name != "__mir__" {
return Ok(None);
}
self.try_build_mir_debug_call(method, arguments)
}
/// Phase 288.1: REPL session variable bridge
/// Transform __repl.get/set → ExternCall("__repl", "get/set", args)
fn try_build_repl_method_call(
&mut self,
object: &ASTNode,
method: &str,
arguments: &[ASTNode],
) -> Result<Option<ValueId>, String> {
let ASTNode::Variable { name: obj_name, .. } = object else {
return Ok(None);
};
if obj_name != "__repl" {
return Ok(None);
}
// Only handle get/set methods
if method != "get" && method != "set" {
return Err(format!("__repl.{} is not supported. Only __repl.get and __repl.set are allowed.", method));
}
// Build argument values
let arg_values = self.build_call_args(arguments)?;
// Emit ExternCall instruction
let dst = self.next_value_id();
self.emit_instruction(MirInstruction::ExternCall {
dst: Some(dst),
iface_name: "__repl".to_string(),
method_name: method.to_string(),
args: arg_values,
effects: EffectMask::PURE, // get/set are pure from MIR perspective
})?;
Ok(Some(dst))
}
fn try_build_static_receiver_method_call(
&mut self,
object: &ASTNode,
method: &str,
arguments: &[ASTNode],
) -> Result<Option<ValueId>, String> {
let ASTNode::Variable { name: obj_name, .. } = object else {
return Ok(None);
};
// Phase 287 P4: Fix toString() method resolution bug
// Guard: If this is a local variable, don't treat as static box name
let is_local_var = self.variable_ctx.variable_map.contains_key(obj_name);
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
eprintln!("[P287-DEBUG] try_build_static_receiver_method_call: obj_name={}, method={}, is_local_var={}", obj_name, method, is_local_var);
eprintln!("[P287-DEBUG] variable_map keys: {:?}", self.variable_ctx.variable_map.keys().collect::<Vec<_>>());
}
if is_local_var {
// This is a variable reference (primitive or box instance), not a static box name
// Let it flow through to handle_standard_method_call (line 147 in build_method_call_impl)
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
eprintln!("[P287-DEBUG] -> Returning None (local var, will use method call)");
}
return Ok(None);
}
// Only treat as static box method call if obj_name is NOT a local variable
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
eprintln!("[P287-DEBUG] -> Calling try_build_static_method_call (not a local var)");
}
self.try_build_static_method_call(obj_name, method, arguments)
}
fn try_normalize_this_me_method_call(
&mut self,
object: &ASTNode,
method: &str,
arguments: &[ASTNode],
) -> Result<Option<ValueId>, String> {
if !matches!(object, ASTNode::This { .. } | ASTNode::Me { .. }) {
return Ok(None);
}
// Priority 1: static box → compile-time static call normalization
if let Some(box_name) = self.comp_ctx.current_static_box.clone() {
if std::env::var("NYASH_TRACE_NORMALIZE").is_ok() {
eprintln!(
"[trace:normalize] this.{}() → {}.{}() (static call)",
method, box_name, method
);
}
// this.method(args) → current_static_box.method/arity(args)
return Ok(Some(self.handle_static_method_call(
&box_name, method, arguments,
)?));
}
// Instance method fallback (requires variable_map["me"])
self.handle_me_method_call(method, arguments)
}
// Build from expression: from Parent.method(arguments)
pub fn build_from_expression(
&mut self,
parent: String,
method: String,
arguments: Vec<ASTNode>,
) -> Result<ValueId, String> {
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.build_expression(arg)?);
}
let parent_value = crate::mir::builder::emission::constant::emit_string(self, parent);
let result_id = self.next_value_id();
self.emit_box_or_plugin_call(
Some(result_id),
parent_value,
method,
None,
arg_values,
EffectMask::READ.add(Effect::ReadHeap),
)?;
Ok(result_id)
}
// ========================================
// Private helper methods (small functions)
// ========================================
/// Try build TypeOp function calls (isType, asType)
fn try_build_typeop_function(
&mut self,
name: &str,
args: &[ASTNode],
) -> Result<Option<ValueId>, String> {
if (name == "isType" || name == "asType") && args.len() == 2 {
if let Some(type_name) = special_handlers::extract_string_literal(&args[1]) {
let val = self.build_expression(args[0].clone())?;
let ty = special_handlers::parse_type_name_to_mir(&type_name);
let dst = self.next_value_id();
let op = if name == "isType" {
TypeOpKind::Check
} else {
TypeOpKind::Cast
};
self.emit_instruction(MirInstruction::TypeOp {
dst,
op,
value: val,
ty,
})?;
return Ok(Some(dst));
}
}
Ok(None)
}
/// Try handle math.* function in function-style (sin/cos/abs/min/max)
fn try_handle_math_function(
&mut self,
name: &str,
raw_args: Vec<ASTNode>,
) -> Option<Result<ValueId, String>> {
if !special_handlers::is_math_function(name) {
return None;
}
// Build numeric args directly for math.* to preserve f64 typing
let mut math_args: Vec<ValueId> = Vec::new();
for a in raw_args.into_iter() {
match a {
ASTNode::New {
class, arguments, ..
} if class == "FloatBox" && arguments.len() == 1 => {
match self.build_expression(arguments[0].clone()) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
}
}
ASTNode::New {
class, arguments, ..
} if class == "IntegerBox" && arguments.len() == 1 => {
let iv = match self.build_expression(arguments[0].clone()) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let fv = self.next_value_id();
if let Err(e) = self.emit_instruction(MirInstruction::TypeOp {
dst: fv,
op: TypeOpKind::Cast,
value: iv,
ty: MirType::Float,
}) {
return Some(Err(e));
}
math_args.push(fv);
}
ASTNode::Literal {
value: LiteralValue::Float(_),
..
} => match self.build_expression(a) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
},
other => match self.build_expression(other) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
},
}
}
// new MathBox()
let math_recv = self.next_value_id();
if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) {
return Some(Err(e));
}
self.type_ctx
.value_origin_newbox
.insert(math_recv, "MathBox".to_string());
// birth()
if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) {
return Some(Err(e));
}
// call method
let dst = self.next_value_id();
if let Err(e) = self.emit_method_call(Some(dst), math_recv, name.to_string(), math_args) {
return Some(Err(e));
}
Some(Ok(dst))
}
/// Try handle env.* extern methods
fn try_handle_env_method(
&mut self,
object: &ASTNode,
method: &str,
arguments: &Vec<ASTNode>,
) -> Option<Result<ValueId, String>> {
let ASTNode::FieldAccess {
object: env_obj,
field: env_field,
..
} = object
else {
return None;
};
if let ASTNode::Variable { name: env_name, .. } = env_obj.as_ref() {
if env_name != "env" {
return None;
}
// Build arguments once
let mut arg_values = Vec::new();
for arg in arguments {
match self.build_expression(arg.clone()) {
Ok(v) => arg_values.push(v),
Err(e) => return Some(Err(e)),
}
}
let iface = env_field.as_str();
let m = method;
let mut extern_call = |iface_name: &str,
method_name: &str,
effects: EffectMask,
returns: bool|
-> Result<ValueId, String> {
let result_id = self.next_value_id();
self.emit_instruction(MirInstruction::ExternCall {
dst: if returns { Some(result_id) } else { None },
iface_name: iface_name.to_string(),
method_name: method_name.to_string(),
args: arg_values.clone(),
effects,
})?;
if returns {
Ok(result_id)
} else {
let void_id = crate::mir::builder::emission::constant::emit_void(self);
Ok(void_id)
}
};
// Use the new module for env method spec
if let Some((iface_name, method_name, effects, returns)) =
super::extern_calls::get_env_method_spec(iface, m)
{
return Some(extern_call(&iface_name, &method_name, effects, returns));
}
return None;
}
None
}
/// Build call arguments from AST
fn build_call_args(&mut self, args: &[ASTNode]) -> Result<Vec<ValueId>, String> {
let mut arg_values = Vec::new();
for a in args {
arg_values.push(self.build_expression(a.clone())?);
}
Ok(arg_values)
}
/// Build str(x) normalization to x.str()
fn build_str_normalization(&mut self, arg: ValueId) -> Result<ValueId, String> {
let dst = self.next_value_id();
// Use unified method emission; downstream rewrite will functionize as needed
self.emit_method_call(Some(dst), arg, "str".to_string(), vec![])?;
Ok(dst)
}
/// Build legacy function call
fn build_legacy_function_call(
&mut self,
name: String,
arg_values: Vec<ValueId>,
) -> Result<ValueId, String> {
let dst = self.next_value_id();
// === ChatGPT5 Pro Design: Type-safe function call resolution ===
let callee = match self.resolve_call_target(&name) {
Ok(c) => c,
Err(_e) => {
// Fallback: unique static method
if let Some(result) = self.try_static_method_fallback(&name, &arg_values)? {
return Ok(result);
}
// Tail-based fallback (disabled by default)
if let Some(result) = self.try_tail_based_fallback(&name, &arg_values)? {
return Ok(result);
}
return Err(format!(
"Unresolved function: '{}'. {}",
name,
super::super::call_resolution::suggest_resolution(&name)
));
}
};
// Legacy compatibility: Create dummy func value for old systems
let fun_val = crate::mir::builder::name_const::make_name_const_result(self, &name)?;
// Emit new-style Call with type-safe callee
self.emit_instruction(MirInstruction::Call {
dst: Some(dst),
func: fun_val,
callee: Some(callee),
args: arg_values,
effects: EffectMask::READ.add(Effect::ReadHeap),
})?;
Ok(dst)
}
/// Build unified function call
fn build_unified_function_call(
&mut self,
name: String,
arg_values: Vec<ValueId>,
) -> Result<ValueId, String> {
let dst = self.next_value_id();
self.emit_unified_call(Some(dst), CallTarget::Global(name), arg_values)?;
Ok(dst)
}
/// Try static method call: BoxName.method(args)
fn try_build_static_method_call(
&mut self,
obj_name: &str,
method: &str,
arguments: &[ASTNode],
) -> Result<Option<ValueId>, String> {
let is_local_var = self.variable_ctx.variable_map.contains_key(obj_name);
// Debug trace
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
trace.stderr_if(
&format!(
"[DEBUG] try_build_static_method_call: obj_name={}, method={}",
obj_name, method
),
true,
);
trace.stderr_if(&format!("[DEBUG] is_local_var={}", is_local_var), true);
if is_local_var {
trace.stderr_if(
&format!(
"[DEBUG] variable_map contains '{}' - treating as local variable, will use method call",
obj_name
),
true,
);
trace.stderr_if(
&format!(
"[DEBUG] variable_map keys: {:?}",
self.variable_ctx.variable_map.keys().collect::<Vec<_>>()
),
true,
);
} else {
trace.stderr_if(
&format!(
"[DEBUG] '{}' not in variable_map - treating as static box, will use global call",
obj_name
),
true,
);
}
}
// Phase 15.5: Treat unknown identifiers in receiver position as static type names
if !is_local_var {
let result = self.handle_static_method_call(obj_name, method, arguments)?;
return Ok(Some(result));
}
Ok(None)
}
/// Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog への変換
///
/// 構文:
/// __mir__.log("label", v1, v2, ...)
/// __mir__.mark("label")
///
/// - 第一引数は String リテラル想定(それ以外はこのハンドラをスキップして通常の解決に回す)。
/// - 戻り値は Void 定数の ValueId式コンテキストでも型破綻しないようにするため
fn try_build_mir_debug_call(
&mut self,
method: &str,
arguments: &[ASTNode],
) -> Result<Option<ValueId>, String> {
if method != "log" && method != "mark" {
return Ok(None);
}
if arguments.is_empty() {
return Err("__mir__.log/__mir__.mark requires at least a label argument".to_string());
}
// 第一引数は String リテラルのみ対応(それ以外は通常経路にフォールバック)
let label = match &arguments[0] {
ASTNode::Literal {
value: LiteralValue::String(s),
..
} => s.clone(),
_ => {
// ラベルがリテラルでない場合はこのハンドラをスキップし、通常の static box 解決に任せる
return Ok(None);
}
};
// 残りの引数を評価して ValueId を集める
let mut values: Vec<ValueId> = Vec::new();
if method == "log" {
for arg in &arguments[1..] {
values.push(self.build_expression(arg.clone())?);
}
}
// MIR に DebugLog 命令を 1 つ挿入(意味論は NYASH_MIR_DEBUG_LOG=1 のときにだけ効く)
self.emit_instruction(MirInstruction::DebugLog {
message: label,
values,
})?;
// 式コンテキスト用に Void 定数を返す(呼び出し元では通常使われない)
let void_id = crate::mir::builder::emission::constant::emit_void(self);
Ok(Some(void_id))
}
/// Try static method fallback (name+arity)
fn try_static_method_fallback(
&mut self,
name: &str,
arg_values: &[ValueId],
) -> Result<Option<ValueId>, String> {
if let Some(cands) = self.comp_ctx.static_method_index.get(name) {
let mut matches: Vec<(String, usize)> = cands
.iter()
.cloned()
.filter(|(_, ar)| *ar == arg_values.len())
.collect();
if matches.len() == 1 {
let (bx, _arity) = matches.remove(0);
let dst = self.next_value_id();
let func_name = format!("{}.{}{}", bx, name, format!("/{}", arg_values.len()));
// Emit unified global call to the lowered static method function
self.emit_unified_call(
Some(dst),
CallTarget::Global(func_name),
arg_values.to_vec(),
)?;
return Ok(Some(dst));
}
}
Ok(None)
}
/// Try tail-based fallback (disabled by default)
fn try_tail_based_fallback(
&mut self,
name: &str,
arg_values: &[ValueId],
) -> Result<Option<ValueId>, String> {
if std::env::var("NYASH_BUILDER_TAIL_RESOLVE").ok().as_deref() == Some("1") {
if let Some(ref module) = self.current_module {
let tail = format!(".{}{}", name, format!("/{}", arg_values.len()));
let mut cands: Vec<String> = module
.functions
.keys()
.filter(|k| k.ends_with(&tail))
.cloned()
.collect();
if cands.len() == 1 {
let func_name = cands.remove(0);
let dst = self.next_value_id();
self.emit_legacy_call(
Some(dst),
CallTarget::Global(func_name),
arg_values.to_vec(),
)?;
return Ok(Some(dst));
}
}
}
Ok(None)
}
/// Debug trace for receiver (if enabled)
fn trace_receiver_if_enabled(&self, object: &ASTNode, object_value: ValueId) {
if std::env::var("NYASH_DEBUG_PARAM_RECEIVER").ok().as_deref() == Some("1") {
if let ASTNode::Variable { name, .. } = object {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
trace.stderr_if(
&format!(
"[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})",
name, object_value.0
),
true,
);
if let Some(origin) = self.type_ctx.value_origin_newbox.get(&object_value) {
trace.stderr_if(&format!("[DEBUG/param-recv] origin: {}", origin), true);
}
if let Some(&mapped_id) = self.variable_ctx.variable_map.get(name) {
trace.stderr_if(
&format!(
"[DEBUG/param-recv] variable_map['{}'] = ValueId({})",
name, mapped_id.0
),
true,
);
if mapped_id != object_value {
trace.stderr_if(
"[DEBUG/param-recv] ⚠️ MISMATCH! build_expression returned different ValueId!",
true,
);
}
} else {
trace.stderr_if(
&format!(
"[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!",
name
),
true,
);
}
trace.stderr_if(
&format!("[DEBUG/param-recv] current_block: {:?}", self.current_block),
true,
);
}
}
}
}