feat(mir): Phase 131-11-E - TypeFacts/TypeDemands 分離(SSOT)
## 実装内容
### 1) Rust MIR Builder (ops.rs + lifecycle.rs)
- OperandTypeClass で型分類(String/Integer/Unknown)
- BinOp 型推論: Integer + Unknown → Integer
- lifecycle.rs に repropagate_binop_types() パス追加
- PHI 型解決後に BinOp 型を再計算
### 2) JSON Emission (mir_json_emit.rs)
- 結果ベースの dst_type 発行に変更
- Integer → "i64", String → {kind: handle, box_type: StringBox}
### 3) Python LLVM Backend (binop.py)
- dst_type を確実な情報として使用
- dst_type != None なら優先して処理
## 結果
- ✅ MIR: PHI/BinOp が Integer として正しく型付け
- ✅ VM: `Result: 3` (正しい出力)
- ✅ JSON: `dst_type: "i64"` を発行
- ❓ LLVM: 別の codegen 問題の可能性あり
## SSOT 設計達成
- TypeFacts(事実): 定義命令から推論
- TypeDemands(要求): 使用箇所の coercion で吸収
- 後方伝播なし: Fail-Fast に統一
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -125,45 +125,54 @@ def lower_binop(
|
||||
# pointer present?
|
||||
is_ptr_side = (hasattr(lhs_raw, 'type') and isinstance(lhs_raw.type, ir.PointerType)) or \
|
||||
(hasattr(rhs_raw, 'type') and isinstance(rhs_raw.type, ir.PointerType))
|
||||
# Phase 131-6 FIX: Do NOT use dst_type hint from MIR JSON!
|
||||
# The dst_type is a forward-looking type hint that may be incorrect
|
||||
# (e.g., "i + 1" gets StringBox hint because i is later used as string in print(i))
|
||||
# We should only do string concat if operands are actually strings.
|
||||
#
|
||||
# OLD CODE (REMOVED):
|
||||
# force_string = False
|
||||
# try:
|
||||
# if isinstance(dst_type, dict) and dst_type.get('kind') == 'handle' and dst_type.get('box_type') == 'StringBox':
|
||||
# force_string = True
|
||||
# except Exception:
|
||||
# pass
|
||||
|
||||
# Phase 196: TypeFacts SSOT - Only check for actual string types (not use-site demands)
|
||||
# Check if BOTH operands are known to be strings from their definition
|
||||
any_tagged = False
|
||||
# Phase 131-11-E: Use dst_type as authoritative hint
|
||||
# After Phase 131-11-E, dst_type is correctly set by BinOp re-propagation
|
||||
# - "i64" means integer arithmetic (even if operands are unknown)
|
||||
# - {"kind": "handle", "box_type": "StringBox"} means string concat
|
||||
# - None/missing means fallback to operand analysis
|
||||
explicit_integer = False
|
||||
explicit_string = False
|
||||
try:
|
||||
if resolver is not None:
|
||||
# Only check string_literals (TypeFacts), NOT is_stringish (TypeDemands)
|
||||
if hasattr(resolver, 'string_literals'):
|
||||
any_tagged = (lhs in resolver.string_literals) or (rhs in resolver.string_literals)
|
||||
# Check if resolver has explicit type information (MirType::String or StringBox)
|
||||
if not any_tagged and hasattr(resolver, 'value_types'):
|
||||
lhs_ty = resolver.value_types.get(lhs)
|
||||
rhs_ty = resolver.value_types.get(rhs)
|
||||
lhs_str = lhs_ty and (lhs_ty.get('kind') == 'string' or
|
||||
(lhs_ty.get('kind') == 'handle' and lhs_ty.get('box_type') == 'StringBox'))
|
||||
rhs_str = rhs_ty and (rhs_ty.get('kind') == 'string' or
|
||||
(rhs_ty.get('kind') == 'handle' and rhs_ty.get('box_type') == 'StringBox'))
|
||||
any_tagged = lhs_str or rhs_str
|
||||
if dst_type == "i64":
|
||||
explicit_integer = True
|
||||
elif isinstance(dst_type, dict) and dst_type.get('kind') == 'handle' and dst_type.get('box_type') == 'StringBox':
|
||||
explicit_string = True
|
||||
except Exception:
|
||||
pass
|
||||
is_str = is_ptr_side or any_tagged
|
||||
|
||||
# Phase 131-6 DEBUG
|
||||
# If explicit type hint is present, use it
|
||||
if explicit_integer:
|
||||
is_str = False
|
||||
elif explicit_string:
|
||||
is_str = True
|
||||
else:
|
||||
# Phase 196: TypeFacts SSOT - Only check for actual string types (not use-site demands)
|
||||
# Check if BOTH operands are known to be strings from their definition
|
||||
any_tagged = False
|
||||
try:
|
||||
if resolver is not None:
|
||||
# Only check string_literals (TypeFacts), NOT is_stringish (TypeDemands)
|
||||
if hasattr(resolver, 'string_literals'):
|
||||
any_tagged = (lhs in resolver.string_literals) or (rhs in resolver.string_literals)
|
||||
# Check if resolver has explicit type information (MirType::String or StringBox)
|
||||
if not any_tagged and hasattr(resolver, 'value_types'):
|
||||
lhs_ty = resolver.value_types.get(lhs)
|
||||
rhs_ty = resolver.value_types.get(rhs)
|
||||
lhs_str = lhs_ty and (lhs_ty.get('kind') == 'string' or
|
||||
(lhs_ty.get('kind') == 'handle' and lhs_ty.get('box_type') == 'StringBox'))
|
||||
rhs_str = rhs_ty and (rhs_ty.get('kind') == 'string' or
|
||||
(rhs_ty.get('kind') == 'handle' and rhs_ty.get('box_type') == 'StringBox'))
|
||||
any_tagged = lhs_str or rhs_str
|
||||
except Exception:
|
||||
pass
|
||||
is_str = is_ptr_side or any_tagged
|
||||
|
||||
# Phase 131-11-E DEBUG
|
||||
if os.environ.get('NYASH_BINOP_DEBUG') == '1':
|
||||
print(f"[binop +] lhs={lhs} rhs={rhs} dst={dst}")
|
||||
print(f" dst_type={dst_type} explicit_integer={explicit_integer} explicit_string={explicit_string}")
|
||||
print(f" is_ptr_side={is_ptr_side} any_tagged={any_tagged} is_str={is_str}")
|
||||
print(f" dst_type={dst_type}")
|
||||
if is_str:
|
||||
# Helper: convert raw or resolved value to string handle
|
||||
def to_handle(raw, val, tag: str, vid: int):
|
||||
|
||||
Reference in New Issue
Block a user