Skip to main content

libperl_macrogen/
type_repr.rs

1//! 型表現モジュール
2//!
3//! TypeConstraint で使用する構造化された型表現を提供する。
4//! C 型、Rust 型、推論結果を統一的に表現し、文字列ベースの型比較を排除する。
5
6use std::fmt;
7
8use crate::ast::BinOp;
9use crate::intern::InternedStr;
10
11// ============================================================================
12// TypeRepr: トップレベル型表現
13// ============================================================================
14
15/// 型表現(出所情報を含む)
16#[derive(Debug, Clone)]
17pub enum TypeRepr {
18    /// C 言語の型(CHeader, Apidoc, InlineFn 共通)
19    CType {
20        /// 型指定子(int, char, struct X, など)
21        specs: CTypeSpecs,
22        /// 派生型(ポインタ、配列など)
23        derived: Vec<CDerivedType>,
24        /// 出所(デバッグ用)
25        source: CTypeSource,
26    },
27
28    /// Rust バインディングからの型(syn::Type 由来)
29    RustType {
30        /// 型表現
31        repr: RustTypeRepr,
32        /// 出所(関数名など)
33        source: RustTypeSource,
34    },
35
36    /// 推論で導出
37    Inferred(InferredType),
38}
39
40// ============================================================================
41// C 型の出所
42// ============================================================================
43
44/// C 型の出所
45#[derive(Debug, Clone)]
46pub enum CTypeSource {
47    /// C ヘッダーのパース結果
48    Header,
49    /// apidoc(embed.fnc 等)- 元の文字列を保持
50    Apidoc { raw: String },
51    /// apidoc_patches の override (return_type_override / arg_type_override)。
52    /// 手書きの修正なので最優先 Tier (0) — bindings 伝播 (Tier 1) にも勝つ。
53    /// AvFILL の return_type_override が callee (mg_size) 由来の Tier 1 I32 に
54    /// 負けていた問題への対処 (GH #15)
55    PatchOverride { raw: String },
56    /// inline 関数の AST
57    InlineFn { func_name: InternedStr },
58    /// parser.rs の parse_type_from_string を使用して解析
59    Parser,
60    /// フィールドアクセスからの逆推論
61    FieldInference { field_name: InternedStr },
62    /// キャスト式の型名(AST から直接変換)
63    Cast,
64    /// SV ファミリーキャストからの型推論
65    SvFamilyCast,
66    /// 共通フィールドマクロ宣言フィールドへのアクセス経路から逆推論された
67    /// SV ファミリー型(例: `xcv_gv_u` (in `_XPVCV_COMMON`) アクセス →
68    /// 引数 `cv` は `*mut CV`)。総称的な `SvFamilyCast` 由来の `*mut SV`
69    /// より優先するため、`confidence_tier` で 3 を返す。
70    CommonMacroFieldInference,
71}
72
73// ============================================================================
74// Rust 型の出所
75// ============================================================================
76
77/// Rust 型の出所
78#[derive(Debug, Clone)]
79pub enum RustTypeSource {
80    /// bindings.rs の関数引数
81    FnParam { func_name: String, param_index: usize },
82    /// bindings.rs の関数戻り値
83    FnReturn { func_name: String },
84    /// bindings.rs の定数
85    Const { const_name: String },
86    /// 文字列からパースされた型(具体的な出所は不明)
87    Parsed { raw: String },
88    /// 確定済みマクロのパラメータ型キャッシュから伝播した型。
89    /// 中身は推論結果なので確度は Tier 4 (apidoc 等の宣言情報より弱い)。
90    Propagated { raw: String },
91}
92
93// ============================================================================
94// C 型の構造化表現
95// ============================================================================
96
97/// C 型指定子(DeclSpecs から抽出)
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum CTypeSpecs {
100    /// void
101    Void,
102    /// char (signed: None = plain char, Some(true) = signed, Some(false) = unsigned)
103    Char { signed: Option<bool> },
104    /// 整数型
105    Int { signed: bool, size: IntSize },
106    /// float
107    Float,
108    /// double (is_long: long double かどうか)
109    Double { is_long: bool },
110    /// _Bool
111    Bool,
112    /// 構造体/共用体
113    Struct { name: Option<InternedStr>, is_union: bool },
114    /// enum
115    Enum { name: Option<InternedStr> },
116    /// typedef 名
117    TypedefName(InternedStr),
118    /// 未解決の typedef 名(interner に登録されていない場合)
119    UnknownTypedef(String),
120}
121
122/// 整数サイズ
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum IntSize {
125    /// short
126    Short,
127    /// int (default)
128    Int,
129    /// long
130    Long,
131    /// long long
132    LongLong,
133    /// __int128
134    Int128,
135}
136
137/// C 派生型
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub enum CDerivedType {
140    /// ポインタ
141    Pointer {
142        is_const: bool,
143        is_volatile: bool,
144        is_restrict: bool,
145    },
146    /// 配列
147    Array { size: Option<usize> },
148    /// 関数
149    Function {
150        params: Vec<CTypeSpecs>,
151        variadic: bool,
152    },
153}
154
155// ============================================================================
156// Rust 型の構造化表現
157// ============================================================================
158
159/// Rust 型表現(syn::Type から変換)
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub enum RustTypeRepr {
162    /// C互換基本型 (c_int, c_char, etc.)
163    CPrimitive(CPrimitiveKind),
164    /// Rust基本型 (i32, u64, bool, etc.)
165    RustPrimitive(RustPrimitiveKind),
166    /// ポインタ (*mut T, *const T)
167    Pointer {
168        inner: Box<RustTypeRepr>,
169        is_const: bool,
170    },
171    /// 参照 (&T, &mut T)
172    Reference {
173        inner: Box<RustTypeRepr>,
174        is_mut: bool,
175    },
176    /// 名前付き型 (SV, AV, PerlInterpreter, etc.)
177    Named(String),
178    /// Option<T>
179    Option(Box<RustTypeRepr>),
180    /// 関数ポインタ
181    FnPointer {
182        params: Vec<RustTypeRepr>,
183        ret: Option<Box<RustTypeRepr>>,
184    },
185    /// ユニット ()
186    Unit,
187    /// パース不能だった型(文字列で保持)
188    Unknown(String),
189}
190
191/// C互換基本型
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub enum CPrimitiveKind {
194    CChar,
195    CSchar,
196    CUchar,
197    CShort,
198    CUshort,
199    CInt,
200    CUint,
201    CLong,
202    CUlong,
203    CLongLong,
204    CUlongLong,
205    CFloat,
206    CDouble,
207}
208
209/// Rust基本型
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum RustPrimitiveKind {
212    I8,
213    I16,
214    I32,
215    I64,
216    I128,
217    Isize,
218    U8,
219    U16,
220    U32,
221    U64,
222    U128,
223    Usize,
224    F32,
225    F64,
226    Bool,
227}
228
229// ============================================================================
230// 推論の根拠 (InferredType)
231// ============================================================================
232
233/// 推論で導出された型
234#[derive(Debug, Clone)]
235pub enum InferredType {
236    // ==================== リテラル ====================
237    /// 整数リテラル (42, 0x1F, etc.)
238    IntLiteral,
239    /// 符号なし整数リテラル (42u, etc.)
240    UIntLiteral,
241    /// 浮動小数点リテラル (3.14, etc.)
242    FloatLiteral,
243    /// 文字リテラル ('a')
244    CharLiteral,
245    /// 文字列リテラル ("hello")
246    StringLiteral,
247
248    // ==================== 識別子参照 ====================
249    /// シンボルテーブルからの参照
250    SymbolLookup {
251        name: InternedStr,
252        /// 解決された型
253        resolved_type: Box<TypeRepr>,
254    },
255    /// THX (my_perl) のデフォルト型
256    ThxDefault,
257
258    // ==================== 演算子 ====================
259    /// 二項演算の結果
260    BinaryOp {
261        op: BinOp,
262        /// 左右オペランドの型から計算された結果型
263        result_type: Box<TypeRepr>,
264    },
265    /// 単項演算 (+, -, ~)
266    UnaryArithmetic {
267        /// 内部式の型をそのまま継承
268        inner_type: Box<TypeRepr>,
269    },
270    /// 論理否定 (!) - 常に int
271    LogicalNot,
272    /// アドレス取得 (&x)
273    AddressOf { inner_type: Box<TypeRepr> },
274    /// 間接参照 (*p)
275    Dereference { pointer_type: Box<TypeRepr> },
276    /// インクリメント/デクリメント (++, --)
277    IncDec { inner_type: Box<TypeRepr> },
278
279    // ==================== メンバーアクセス ====================
280    /// 直接メンバーアクセス (expr.member)
281    MemberAccess {
282        base_type: String,
283        member: InternedStr,
284        /// 解決されたフィールド型
285        field_type: Option<Box<TypeRepr>>,
286    },
287    /// ポインタメンバーアクセス (expr->member)
288    PtrMemberAccess {
289        base_type: String,
290        member: InternedStr,
291        /// 解決されたフィールド型
292        field_type: Option<Box<TypeRepr>>,
293        /// 一致型を使用した場合(ベース型が不明時)
294        used_consistent_type: bool,
295    },
296
297    // ==================== 配列/添字 ====================
298    /// 配列添字 (arr[i])
299    ArraySubscript {
300        base_type: Box<TypeRepr>,
301        /// 要素型
302        element_type: Box<TypeRepr>,
303    },
304
305    // ==================== 条件・制御 ====================
306    /// 条件演算子 (cond ? then : else)
307    Conditional {
308        then_type: Box<TypeRepr>,
309        else_type: Box<TypeRepr>,
310        /// 計算された共通型
311        result_type: Box<TypeRepr>,
312    },
313    /// コンマ式 (a, b)
314    Comma {
315        /// 右辺の型
316        rhs_type: Box<TypeRepr>,
317    },
318    /// 代入式 (a = b)
319    Assignment {
320        /// 左辺の型
321        lhs_type: Box<TypeRepr>,
322    },
323
324    // ==================== 型操作 ====================
325    /// キャスト式 ((type)expr)
326    Cast { target_type: Box<TypeRepr> },
327    /// sizeof 式/型 - 常に unsigned long
328    Sizeof,
329    /// alignof - 常に unsigned long
330    Alignof,
331    /// 複合リテラル ((type){...})
332    CompoundLiteral { type_name: Box<TypeRepr> },
333
334    // ==================== その他 ====================
335    /// 文式 ({ ... })
336    StmtExpr {
337        /// 最後の式の型
338        last_expr_type: Option<Box<TypeRepr>>,
339    },
340    /// アサーション - 常に void
341    Assert,
342    /// 関数呼び出しの戻り値(RustBindings/Apidoc から取得できなかった場合)
343    FunctionReturn { func_name: InternedStr },
344}
345
346// ============================================================================
347// 変換関数
348// ============================================================================
349
350impl CTypeSpecs {
351    /// DeclSpecs から CTypeSpecs を抽出
352    pub fn from_decl_specs(specs: &crate::ast::DeclSpecs, _interner: &crate::intern::StringInterner) -> Self {
353        use crate::ast::TypeSpec;
354
355        let mut has_signed = false;
356        let mut has_unsigned = false;
357        let mut has_short = false;
358        let mut has_long: u8 = 0;
359        let mut base_type: Option<CTypeSpecs> = None;
360
361        for type_spec in &specs.type_specs {
362            match type_spec {
363                TypeSpec::Void => base_type = Some(CTypeSpecs::Void),
364                TypeSpec::Char => {
365                    // char の signed/unsigned は後で決定
366                    if base_type.is_none() {
367                        base_type = Some(CTypeSpecs::Char { signed: None });
368                    }
369                }
370                TypeSpec::Short => has_short = true,
371                TypeSpec::Int => {
372                    if base_type.is_none() {
373                        base_type = Some(CTypeSpecs::Int {
374                            signed: true,
375                            size: IntSize::Int,
376                        });
377                    }
378                }
379                TypeSpec::Long => has_long += 1,
380                TypeSpec::Float => base_type = Some(CTypeSpecs::Float),
381                TypeSpec::Double => base_type = Some(CTypeSpecs::Double { is_long: false }),
382                TypeSpec::Signed => has_signed = true,
383                TypeSpec::Unsigned => has_unsigned = true,
384                TypeSpec::Bool => base_type = Some(CTypeSpecs::Bool),
385                TypeSpec::Int128 => {
386                    base_type = Some(CTypeSpecs::Int {
387                        signed: !has_unsigned,
388                        size: IntSize::Int128,
389                    });
390                }
391                TypeSpec::Struct(s) => {
392                    base_type = Some(CTypeSpecs::Struct {
393                        name: s.name,
394                        is_union: false,
395                    });
396                }
397                TypeSpec::Union(s) => {
398                    base_type = Some(CTypeSpecs::Struct {
399                        name: s.name,
400                        is_union: true,
401                    });
402                }
403                TypeSpec::Enum(e) => {
404                    base_type = Some(CTypeSpecs::Enum { name: e.name });
405                }
406                TypeSpec::TypedefName(name) => {
407                    base_type = Some(CTypeSpecs::TypedefName(*name));
408                }
409                _ => {}
410            }
411        }
412
413        // signed/unsigned と short/long の組み合わせを処理
414        if has_short {
415            return CTypeSpecs::Int {
416                signed: !has_unsigned,
417                size: IntSize::Short,
418            };
419        }
420
421        if has_long >= 2 {
422            return CTypeSpecs::Int {
423                signed: !has_unsigned,
424                size: IntSize::LongLong,
425            };
426        }
427
428        if has_long == 1 {
429            if let Some(CTypeSpecs::Double { .. }) = base_type {
430                return CTypeSpecs::Double { is_long: true };
431            }
432            return CTypeSpecs::Int {
433                signed: !has_unsigned,
434                size: IntSize::Long,
435            };
436        }
437
438        // char の signed/unsigned を確定
439        if let Some(CTypeSpecs::Char { .. }) = base_type {
440            if has_signed {
441                return CTypeSpecs::Char { signed: Some(true) };
442            } else if has_unsigned {
443                return CTypeSpecs::Char { signed: Some(false) };
444            }
445            return CTypeSpecs::Char { signed: None };
446        }
447
448        // 単独の signed/unsigned
449        if has_unsigned && base_type.is_none() {
450            return CTypeSpecs::Int {
451                signed: false,
452                size: IntSize::Int,
453            };
454        }
455        if has_signed && base_type.is_none() {
456            return CTypeSpecs::Int {
457                signed: true,
458                size: IntSize::Int,
459            };
460        }
461
462        // int の unsigned
463        if has_unsigned {
464            if let Some(CTypeSpecs::Int { size, .. }) = base_type {
465                return CTypeSpecs::Int {
466                    signed: false,
467                    size,
468                };
469            }
470        }
471
472        base_type.unwrap_or(CTypeSpecs::Int {
473            signed: true,
474            size: IntSize::Int,
475        })
476    }
477}
478
479/// C の「指し先 const」を Rust の `*const` 位相に合わせてシフトする。
480///
481/// Rust の `*const`/`*mut` は **指し先** の const 性を表すが、C の宣言では
482/// - 最内ポインタの指し先 const = **base 型の const 修飾** (`const char *`)
483/// - 外側ポインタの指し先 const = **一つ内側のポインタ自身の const 修飾**
484///   (`char * const *`)
485///
486/// `CDerivedType::from_derived_decls` は各 Pointer に C の「ポインタ自身の
487/// 修飾」を保持したままなので、このシフトを通して Rust 表記 (renderer は
488/// Pointer.is_const をそのまま `*const` にする) の意味に正規化する。
489/// 最外ポインタ自身の C 修飾 (束縛の const) は Rust ポインタ型には現れない
490/// ため捨てる。derived は外→内順であることを前提とする (GH #15)。
491pub fn shift_pointee_const(derived: &mut [CDerivedType], base_is_const: bool) {
492    let ptr_idxs: Vec<usize> = derived
493        .iter()
494        .enumerate()
495        .filter(|(_, d)| matches!(d, CDerivedType::Pointer { .. }))
496        .map(|(i, _)| i)
497        .collect();
498    if ptr_idxs.is_empty() {
499        return;
500    }
501    let old_quals: Vec<bool> = ptr_idxs
502        .iter()
503        .map(|&i| match &derived[i] {
504            CDerivedType::Pointer { is_const, .. } => *is_const,
505            _ => unreachable!(),
506        })
507        .collect();
508    for (j, &i) in ptr_idxs.iter().enumerate() {
509        // 最内 (末尾) = base の const、それ以外 = 一つ内側の旧 C 修飾
510        let new_const = if j + 1 == ptr_idxs.len() {
511            base_is_const
512        } else {
513            old_quals[j + 1]
514        };
515        if let CDerivedType::Pointer { is_const, .. } = &mut derived[i] {
516            *is_const = new_const;
517        }
518    }
519}
520
521impl CDerivedType {
522    /// DerivedDecl のリストから CDerivedType のリストを作成し、
523    /// base 型の const 修飾を含めて Rust の `*const` 位相へ正規化する。
524    /// (`const char *` → 最内 Pointer.is_const = true。GH #15)
525    pub fn from_derived_decls_with_base_const(
526        derived: &[crate::ast::DerivedDecl],
527        base_is_const: bool,
528    ) -> Vec<Self> {
529        let mut v = Self::from_derived_decls(derived);
530        shift_pointee_const(&mut v, base_is_const);
531        v
532    }
533
534    /// DerivedDecl のリストから CDerivedType のリストを作成
535    pub fn from_derived_decls(derived: &[crate::ast::DerivedDecl]) -> Vec<Self> {
536        use crate::ast::ExprKind;
537
538        derived
539            .iter()
540            .map(|d| match d {
541                crate::ast::DerivedDecl::Pointer(quals) => CDerivedType::Pointer {
542                    is_const: quals.is_const,
543                    is_volatile: quals.is_volatile,
544                    is_restrict: quals.is_restrict,
545                },
546                crate::ast::DerivedDecl::Array(array_decl) => {
547                    // 配列サイズが定数リテラルの場合のみ抽出
548                    let size = array_decl.size.as_ref().and_then(|expr| {
549                        match &expr.kind {
550                            ExprKind::IntLit(n) => Some(*n as usize),
551                            ExprKind::UIntLit(n) => Some(*n as usize),
552                            _ => None,
553                        }
554                    });
555                    CDerivedType::Array { size }
556                }
557                crate::ast::DerivedDecl::Function(_params) => {
558                    // 関数パラメータの詳細は簡略化
559                    CDerivedType::Function {
560                        params: vec![],
561                        variadic: false,
562                    }
563                }
564            })
565            .collect()
566    }
567}
568
569impl RustTypeRepr {
570    /// 型文字列から RustTypeRepr をパース
571    pub fn from_type_string(s: &str) -> Self {
572        let s = s.trim();
573
574        // ユニット型
575        if s == "()" {
576            return RustTypeRepr::Unit;
577        }
578
579        // ポインタ型
580        if let Some(rest) = s.strip_prefix("*mut ") {
581            return RustTypeRepr::Pointer {
582                inner: Box::new(Self::from_type_string(rest)),
583                is_const: false,
584            };
585        }
586        if let Some(rest) = s.strip_prefix("* mut ") {
587            return RustTypeRepr::Pointer {
588                inner: Box::new(Self::from_type_string(rest)),
589                is_const: false,
590            };
591        }
592        if let Some(rest) = s.strip_prefix("*const ") {
593            return RustTypeRepr::Pointer {
594                inner: Box::new(Self::from_type_string(rest)),
595                is_const: true,
596            };
597        }
598        if let Some(rest) = s.strip_prefix("* const ") {
599            return RustTypeRepr::Pointer {
600                inner: Box::new(Self::from_type_string(rest)),
601                is_const: true,
602            };
603        }
604
605        // 参照型
606        if let Some(rest) = s.strip_prefix("&mut ") {
607            return RustTypeRepr::Reference {
608                inner: Box::new(Self::from_type_string(rest)),
609                is_mut: true,
610            };
611        }
612        if let Some(rest) = s.strip_prefix("& mut ") {
613            return RustTypeRepr::Reference {
614                inner: Box::new(Self::from_type_string(rest)),
615                is_mut: true,
616            };
617        }
618        if let Some(rest) = s.strip_prefix('&') {
619            return RustTypeRepr::Reference {
620                inner: Box::new(Self::from_type_string(rest.trim())),
621                is_mut: false,
622            };
623        }
624
625        // C 互換基本型
626        if let Some(kind) = Self::parse_c_primitive(s) {
627            return RustTypeRepr::CPrimitive(kind);
628        }
629
630        // Rust 基本型
631        if let Some(kind) = Self::parse_rust_primitive(s) {
632            return RustTypeRepr::RustPrimitive(kind);
633        }
634
635        // Option<T>
636        if s.starts_with("Option<") || s.starts_with(":: std :: option :: Option<") {
637            if let Some(inner) = Self::extract_generic_param(s, "Option") {
638                return RustTypeRepr::Option(Box::new(Self::from_type_string(&inner)));
639            }
640        }
641
642        // 名前付き型(識別子)
643        if s.chars().next().map(|c| c.is_alphabetic() || c == '_').unwrap_or(false) {
644            // パスセパレータを含む場合は最後の部分を使用
645            let name = s.split("::").last().unwrap_or(s).trim();
646            return RustTypeRepr::Named(name.to_string());
647        }
648
649        // パース不能
650        RustTypeRepr::Unknown(s.to_string())
651    }
652
653    /// C 互換基本型をパース
654    fn parse_c_primitive(s: &str) -> Option<CPrimitiveKind> {
655        // :: std :: os :: raw :: c_* 形式にも対応
656        let s = s.trim();
657        let name = if s.contains("::") {
658            s.split("::").last()?.trim()
659        } else {
660            s
661        };
662
663        match name {
664            "c_char" => Some(CPrimitiveKind::CChar),
665            "c_schar" => Some(CPrimitiveKind::CSchar),
666            "c_uchar" => Some(CPrimitiveKind::CUchar),
667            "c_short" => Some(CPrimitiveKind::CShort),
668            "c_ushort" => Some(CPrimitiveKind::CUshort),
669            "c_int" => Some(CPrimitiveKind::CInt),
670            "c_uint" => Some(CPrimitiveKind::CUint),
671            "c_long" => Some(CPrimitiveKind::CLong),
672            "c_ulong" => Some(CPrimitiveKind::CUlong),
673            "c_longlong" => Some(CPrimitiveKind::CLongLong),
674            "c_ulonglong" => Some(CPrimitiveKind::CUlongLong),
675            "c_float" => Some(CPrimitiveKind::CFloat),
676            "c_double" => Some(CPrimitiveKind::CDouble),
677            _ => None,
678        }
679    }
680
681    /// Rust 基本型をパース
682    fn parse_rust_primitive(s: &str) -> Option<RustPrimitiveKind> {
683        match s.trim() {
684            "i8" => Some(RustPrimitiveKind::I8),
685            "i16" => Some(RustPrimitiveKind::I16),
686            "i32" => Some(RustPrimitiveKind::I32),
687            "i64" => Some(RustPrimitiveKind::I64),
688            "i128" => Some(RustPrimitiveKind::I128),
689            "isize" => Some(RustPrimitiveKind::Isize),
690            "u8" => Some(RustPrimitiveKind::U8),
691            "u16" => Some(RustPrimitiveKind::U16),
692            "u32" => Some(RustPrimitiveKind::U32),
693            "u64" => Some(RustPrimitiveKind::U64),
694            "u128" => Some(RustPrimitiveKind::U128),
695            "usize" => Some(RustPrimitiveKind::Usize),
696            "f32" => Some(RustPrimitiveKind::F32),
697            "f64" => Some(RustPrimitiveKind::F64),
698            "bool" => Some(RustPrimitiveKind::Bool),
699            _ => None,
700        }
701    }
702
703    /// ジェネリック型のパラメータを抽出
704    fn extract_generic_param(s: &str, type_name: &str) -> Option<String> {
705        // "Option<T>" または ":: std :: option :: Option<T>" から T を抽出
706        let start = s.find(&format!("{}<", type_name))?;
707        let after_open = start + type_name.len() + 1;
708        let content = &s[after_open..];
709
710        // 対応する > を探す(ネストを考慮)
711        let mut depth = 1;
712        let mut end = 0;
713        for (i, c) in content.char_indices() {
714            match c {
715                '<' => depth += 1,
716                '>' => {
717                    depth -= 1;
718                    if depth == 0 {
719                        end = i;
720                        break;
721                    }
722                }
723                _ => {}
724            }
725        }
726
727        if end > 0 {
728            Some(content[..end].trim().to_string())
729        } else {
730            None
731        }
732    }
733}
734
735impl TypeRepr {
736    /// 出所の表示用文字列を取得
737    pub fn source_display(&self) -> &'static str {
738        match self {
739            TypeRepr::CType { source, .. } => match source {
740                CTypeSource::Header => "c-header",
741                CTypeSource::Apidoc { .. } => "apidoc",
742                CTypeSource::PatchOverride { .. } => "patch-override",
743                CTypeSource::InlineFn { .. } => "inline-fn",
744                CTypeSource::Parser => "parser",
745                CTypeSource::FieldInference { .. } => "field-inference",
746                CTypeSource::Cast => "cast",
747                CTypeSource::SvFamilyCast => "sv-family-cast",
748                CTypeSource::CommonMacroFieldInference => "common-macro-field-inference",
749            },
750            TypeRepr::RustType { .. } => "rust-bindings",
751            TypeRepr::Inferred(_) => "inferred",
752        }
753    }
754
755    /// void 型かどうかを判定
756    ///
757    /// ポインタを含まない void 型の場合に true を返す。
758    /// `void *` は false を返す(有効なポインタ型のため)。
759    /// bindings.rs の FnParam ソースかどうか
760    pub fn is_fn_param_source(&self) -> bool {
761        matches!(self, TypeRepr::RustType { source: RustTypeSource::FnParam { .. }, .. })
762    }
763
764    /// 型情報の確度 Tier を返す
765    ///
766    /// - Tier 1: bindings.rs (bindgen生成、変更不可)
767    /// - Tier 2: C ヘッダー宣言 / inline 関数パラメータ (変更不可)
768    /// - Tier 3: apidoc (embed.fnc 等、参考情報)
769    /// - Tier 4: 推論結果 (変更可能)
770    pub fn confidence_tier(&self) -> u8 {
771        match self {
772            TypeRepr::RustType { source, .. } => match source {
773                RustTypeSource::FnParam { .. }
774                | RustTypeSource::FnReturn { .. }
775                | RustTypeSource::Const { .. } => 1,
776                RustTypeSource::Parsed { .. } => 3,
777                RustTypeSource::Propagated { .. } => 4,
778            },
779            TypeRepr::CType { source, .. } => match source {
780                CTypeSource::PatchOverride { .. } => 0,
781                CTypeSource::InlineFn { .. } | CTypeSource::Header => 2,
782                CTypeSource::Apidoc { .. }
783                | CTypeSource::CommonMacroFieldInference => 3,
784                CTypeSource::Cast
785                | CTypeSource::SvFamilyCast
786                | CTypeSource::FieldInference { .. }
787                | CTypeSource::Parser => 4,
788            },
789            TypeRepr::Inferred(_) => 4,
790        }
791    }
792
793    pub fn is_void(&self) -> bool {
794        match self {
795            TypeRepr::CType { specs, derived, .. } => {
796                // ポインタや配列がない純粋な void のみ true
797                derived.is_empty() && matches!(specs, CTypeSpecs::Void)
798            }
799            TypeRepr::RustType { repr, .. } => {
800                matches!(repr, RustTypeRepr::Unit)
801            }
802            TypeRepr::Inferred(inferred) => {
803                match inferred {
804                    InferredType::SymbolLookup { resolved_type, .. } => {
805                        resolved_type.is_void()
806                    }
807                    _ => false,
808                }
809            }
810        }
811    }
812
813    /// 最外ポインタの is_const を true に変更する
814    /// 最外ポインタの is_const を false に変更する(must-mut 用)
815    pub fn make_outer_pointer_mut(&mut self) {
816        match self {
817            TypeRepr::CType { derived, .. } => {
818                for d in derived.iter_mut().rev() {
819                    if let CDerivedType::Pointer { is_const, .. } = d {
820                        *is_const = false;
821                        return;
822                    }
823                }
824            }
825            TypeRepr::RustType { repr, .. } => {
826                if let RustTypeRepr::Pointer { is_const, .. } = repr {
827                    *is_const = false;
828                }
829            }
830            _ => {}
831        }
832    }
833
834    pub fn make_outer_pointer_const(&mut self) {
835        match self {
836            TypeRepr::CType { derived, .. } => {
837                // derived の最後(最外側)のポインタを const に
838                for d in derived.iter_mut().rev() {
839                    if let CDerivedType::Pointer { is_const, .. } = d {
840                        *is_const = true;
841                        return;
842                    }
843                }
844            }
845            TypeRepr::RustType { repr, .. } => {
846                repr.make_outer_pointer_const();
847            }
848            TypeRepr::Inferred(inferred) => {
849                match inferred {
850                    InferredType::SymbolLookup { resolved_type, .. } => {
851                        resolved_type.make_outer_pointer_const();
852                    }
853                    InferredType::Cast { target_type } => {
854                        target_type.make_outer_pointer_const();
855                    }
856                    _ => {}
857                }
858            }
859        }
860    }
861
862    /// ポインタ型かどうか (`has_outer_pointer` のエイリアス)。
863    ///
864    /// `Inferred` ラッパは `resolved_type()` を経由して中身を再帰参照する点で
865    /// `has_outer_pointer` と挙動が異なる (本メソッドは構造的「実体型」判定に
866    /// 使う)。`has_outer_pointer` 自体は既存の使用箇所が `Inferred` を別扱い
867    /// しているため挙動を変えない。
868    pub fn is_pointer_type(&self) -> bool {
869        match self {
870            TypeRepr::CType { derived, .. } => {
871                derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
872            }
873            TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
874            TypeRepr::Inferred(inferred) => inferred
875                .resolved_type()
876                .is_some_and(|t| t.is_pointer_type()),
877        }
878    }
879
880    /// `void *` / `*mut c_void` / `*const c_void` かどうかを **構造的に** 判定する。
881    ///
882    /// 文字列 `contains("void")` ではなく specs/derived の構造で判定するので、
883    /// `*mut struct void_table` のような偽陽性に引っかからない。
884    /// `Inferred` ラッパは `resolved_type()` 経由で中身を再帰参照する。
885    pub fn is_void_pointer(&self) -> bool {
886        match self {
887            TypeRepr::CType { specs, derived, .. } => {
888                derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
889                    && matches!(specs, CTypeSpecs::Void)
890            }
891            TypeRepr::RustType { repr, .. } => match repr {
892                RustTypeRepr::Pointer { inner, .. } => {
893                    matches!(inner.as_ref(), RustTypeRepr::Unit)
894                        || matches!(inner.as_ref(), RustTypeRepr::Named(n) if n == "c_void")
895                }
896                _ => false,
897            },
898            TypeRepr::Inferred(inferred) => inferred
899                .resolved_type()
900                .is_some_and(|t| t.is_void_pointer()),
901        }
902    }
903
904    /// 具体的なポインタ (`void *` ではないポインタ型) かどうか
905    pub fn is_concrete_pointer(&self) -> bool {
906        self.is_pointer_type() && !self.is_void_pointer()
907    }
908
909    /// 最外ポインタを持つかどうか
910    ///
911    /// 既存呼出側の挙動を変えないため `Inferred` は false を返す
912    /// (再帰判定が必要な場合は `is_pointer_type` を使うこと)。
913    pub fn has_outer_pointer(&self) -> bool {
914        match self {
915            TypeRepr::CType { derived, .. } => {
916                derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
917            }
918            TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
919            _ => false,
920        }
921    }
922
923    /// Apidoc の型文字列から TypeRepr を作成
924    pub fn from_apidoc_string(s: &str, interner: &crate::intern::StringInterner) -> Self {
925        // C 型文字列をパース
926        let (specs, derived) = Self::parse_c_type_string(s, interner);
927        TypeRepr::CType {
928            specs,
929            derived,
930            source: CTypeSource::Apidoc { raw: s.to_string() },
931        }
932    }
933
934    /// Rust 形式の型文字列から TypeRepr を作成
935    ///
936    /// `*mut T`, `*const T`, `c_int` などの Rust 形式の型文字列をパースする。
937    /// rust_decl.rs からの型情報の読み込みに使用する。
938    pub fn from_rust_string(s: &str) -> Self {
939        let repr = RustTypeRepr::from_type_string(s);
940        TypeRepr::RustType {
941            repr,
942            source: RustTypeSource::Parsed {
943                raw: s.to_string(),
944            },
945        }
946    }
947
948    /// **伝播型用**: 確定済みマクロのパラメータ型キャッシュから取り出した
949    /// Rust 形式の型文字列をパースする。`from_rust_string` と同じ表現に
950    /// なるが、出所が推論結果であることを保持し Tier 4 として扱う
951    /// (apidoc の宣言 (Tier 3) を上書きしない)。
952    pub fn from_rust_string_propagated(s: &str) -> Self {
953        let repr = RustTypeRepr::from_type_string(s);
954        TypeRepr::RustType {
955            repr,
956            source: RustTypeSource::Propagated {
957                raw: s.to_string(),
958            },
959        }
960    }
961
962    /// **構造ベース**: `UnifiedType` から `TypeRepr` を直接構築する。
963    ///
964    /// bindings.rs (`syn::File`) → `RustField.uty` (`UnifiedType`) で得た
965    /// 構造化情報を、文字列を経由せず TypeRepr に変換する。
966    /// Pointer / Array / Named / 基本型は `CType` として表現し、
967    /// FnPtr / Verbatim / Unknown は表現できないので `RustType` の
968    /// `Unknown(canonical_string)` にフォールバックする。
969    ///
970    /// Source は `CTypeSource::Apidoc { raw }` で記録する (tier 3 相当)。
971    /// bindings は本来 tier 1 だが、本メソッドの呼出元 (anonymous union
972    /// メンバ解決) は元の C-side フィールド型を補完する用途なので、tier 3
973    /// で十分。tier 1 が必要な経路ができたら別 source variant を追加する。
974    pub fn from_unified_type(
975        ut: &crate::unified_type::UnifiedType,
976        interner: &crate::intern::StringInterner,
977    ) -> Self {
978        let raw = ut.to_rust_string();
979        if let Some((specs, derived)) = unified_to_c(ut, interner) {
980            return TypeRepr::CType {
981                specs,
982                derived,
983                source: CTypeSource::Apidoc { raw },
984            };
985        }
986        TypeRepr::RustType {
987            repr: RustTypeRepr::Unknown(raw.clone()),
988            source: RustTypeSource::Parsed { raw },
989        }
990    }
991
992    /// DeclSpecs と Declarator から TypeRepr を作成
993    ///
994    /// C ヘッダーのパース結果から直接 TypeRepr を生成する。
995    /// fields_dict.rs でのフィールド型収集に使用する。
996    pub fn from_decl(
997        specs: &crate::ast::DeclSpecs,
998        declarator: &crate::ast::Declarator,
999        _interner: &crate::intern::StringInterner,
1000    ) -> Self {
1001        let c_specs = CTypeSpecs::from_decl_specs(specs, _interner);
1002        let derived = CDerivedType::from_derived_decls_with_base_const(
1003            &declarator.derived, specs.qualifiers.is_const);
1004        TypeRepr::CType {
1005            specs: c_specs,
1006            derived,
1007            source: CTypeSource::Header,
1008        }
1009    }
1010
1011    /// TypeName (パーサー出力) から TypeRepr を作成
1012    ///
1013    /// `parser::parse_type_from_string` の結果から TypeRepr を生成する。
1014    /// `from_apidoc_string` の代替として使用し、完全な C パーサーを活用する。
1015    pub fn from_type_name(
1016        type_name: &crate::ast::TypeName,
1017        interner: &crate::intern::StringInterner,
1018    ) -> Self {
1019        let c_specs = CTypeSpecs::from_decl_specs(&type_name.specs, interner);
1020        let derived = type_name.declarator
1021            .as_ref()
1022            .map(|d| CDerivedType::from_derived_decls_with_base_const(
1023                &d.derived, type_name.specs.qualifiers.is_const))
1024            .unwrap_or_default();
1025        TypeRepr::CType {
1026            specs: c_specs,
1027            derived,
1028            source: CTypeSource::Parser,
1029        }
1030    }
1031
1032    /// C 型文字列から TypeRepr を作成(パーサー版)
1033    ///
1034    /// `parser.rs` の `parse_type_from_string` を使用して完全な C パーサーで解析する。
1035    /// `files` と `typedefs` が必要なため、`SemanticAnalyzer` など型情報が揃っている
1036    /// コンテキストでの使用を推奨。
1037    ///
1038    /// パースに失敗した場合は `from_apidoc_string` と同じ簡易パーサーにフォールバックする。
1039    pub fn from_c_type_string(
1040        s: &str,
1041        interner: &crate::intern::StringInterner,
1042        files: &crate::source::FileRegistry,
1043        typedefs: &std::collections::HashSet<crate::intern::InternedStr>,
1044    ) -> Self {
1045        use crate::parser::parse_type_from_string;
1046
1047        match parse_type_from_string(s, interner, files, typedefs) {
1048            Ok(type_name) => Self::from_type_name(&type_name, interner),
1049            Err(_) => {
1050                // フォールバック: 既存の簡易パーサーを使用
1051                let (specs, derived) = Self::parse_c_type_string(s, interner);
1052                TypeRepr::CType {
1053                    specs,
1054                    derived,
1055                    source: CTypeSource::Apidoc { raw: s.to_string() },
1056                }
1057            }
1058        }
1059    }
1060
1061    /// C 型文字列をパース(簡易版)
1062    fn parse_c_type_string(s: &str, interner: &crate::intern::StringInterner) -> (CTypeSpecs, Vec<CDerivedType>) {
1063        let s = s.trim();
1064
1065        // Rust 形式 (`*mut T` / `*const T`) は先頭から prefix で剥がす。
1066        // bindings.rs (`RustField.ty`) 由来の文字列が経由する経路で必要。
1067        // 通常の C 形式 (`T *`) と混在しないよう、先頭プレフィクスがある間
1068        // 繰り返し処理する。
1069        let mut prefix_pointers: Vec<bool> = Vec::new(); // is_const
1070        let mut current = s;
1071        loop {
1072            if let Some(rest) = current.strip_prefix("*mut ") {
1073                prefix_pointers.push(false);
1074                current = rest.trim();
1075            } else if let Some(rest) = current.strip_prefix("*const ") {
1076                prefix_pointers.push(true);
1077                current = rest.trim();
1078            } else {
1079                break;
1080            }
1081        }
1082
1083        // ポインタ数をカウント
1084        let mut ptr_count = 0;
1085        let mut is_const = false;
1086        let mut base = current;
1087
1088        // 末尾の * をカウント
1089        while base.ends_with('*') {
1090            ptr_count += 1;
1091            base = base[..base.len() - 1].trim();
1092        }
1093
1094        // "const" をチェック
1095        if base.starts_with("const ") {
1096            is_const = true;
1097            base = base[6..].trim();
1098        }
1099        if base.ends_with(" const") {
1100            is_const = true;
1101            base = base[..base.len() - 6].trim();
1102        }
1103
1104        // 基本型をパース
1105        let specs = Self::parse_c_base_type(base, interner);
1106
1107        // 派生型を構築
1108        // 内側の Rust prefix ポインタを最初に積み、続いて C 形式 trailing
1109        // ポインタを積む。`*mut HV` (Rust) は `HV *` (C) と等価なので
1110        // 出力 derived は同じ並びになる。
1111        let mut derived: Vec<CDerivedType> = Vec::with_capacity(prefix_pointers.len() + ptr_count);
1112        for is_const_p in prefix_pointers.iter().rev() {
1113            derived.push(CDerivedType::Pointer {
1114                is_const: *is_const_p,
1115                is_volatile: false,
1116                is_restrict: false,
1117            });
1118        }
1119        for i in 0..ptr_count {
1120            derived.push(CDerivedType::Pointer {
1121                // Rust の *const は「指し先の const」。base が const なら
1122                // 最内 (最後に push される) ポインタに付ける
1123                is_const: i + 1 == ptr_count && is_const,
1124                is_volatile: false,
1125                is_restrict: false,
1126            });
1127        }
1128
1129        (specs, derived)
1130    }
1131
1132    /// C 基本型文字列をパース
1133    fn parse_c_base_type(s: &str, interner: &crate::intern::StringInterner) -> CTypeSpecs {
1134        match s {
1135            "void" => CTypeSpecs::Void,
1136            "char" => CTypeSpecs::Char { signed: None },
1137            "signed char" => CTypeSpecs::Char { signed: Some(true) },
1138            "unsigned char" => CTypeSpecs::Char { signed: Some(false) },
1139            "short" | "short int" | "signed short" | "signed short int" => {
1140                CTypeSpecs::Int { signed: true, size: IntSize::Short }
1141            }
1142            "unsigned short" | "unsigned short int" => {
1143                CTypeSpecs::Int { signed: false, size: IntSize::Short }
1144            }
1145            "int" | "signed" | "signed int" => {
1146                CTypeSpecs::Int { signed: true, size: IntSize::Int }
1147            }
1148            "unsigned" | "unsigned int" => {
1149                CTypeSpecs::Int { signed: false, size: IntSize::Int }
1150            }
1151            "long" | "long int" | "signed long" | "signed long int" => {
1152                CTypeSpecs::Int { signed: true, size: IntSize::Long }
1153            }
1154            "unsigned long" | "unsigned long int" => {
1155                CTypeSpecs::Int { signed: false, size: IntSize::Long }
1156            }
1157            "long long" | "long long int" | "signed long long" | "signed long long int" => {
1158                CTypeSpecs::Int { signed: true, size: IntSize::LongLong }
1159            }
1160            "unsigned long long" | "unsigned long long int" => {
1161                CTypeSpecs::Int { signed: false, size: IntSize::LongLong }
1162            }
1163            "float" => CTypeSpecs::Float,
1164            "double" => CTypeSpecs::Double { is_long: false },
1165            "long double" => CTypeSpecs::Double { is_long: true },
1166            "_Bool" | "bool" => CTypeSpecs::Bool,
1167            _ => {
1168                // 構造体/共用体/typedef 名として扱う
1169                if let Some(rest) = s.strip_prefix("struct ") {
1170                    if let Some(name) = interner.lookup(rest.trim()) {
1171                        return CTypeSpecs::Struct { name: Some(name), is_union: false };
1172                    }
1173                    return CTypeSpecs::Struct { name: None, is_union: false };
1174                }
1175                if let Some(rest) = s.strip_prefix("union ") {
1176                    if let Some(name) = interner.lookup(rest.trim()) {
1177                        return CTypeSpecs::Struct { name: Some(name), is_union: true };
1178                    }
1179                    return CTypeSpecs::Struct { name: None, is_union: true };
1180                }
1181                if let Some(rest) = s.strip_prefix("enum ") {
1182                    if let Some(name) = interner.lookup(rest.trim()) {
1183                        return CTypeSpecs::Enum { name: Some(name) };
1184                    }
1185                    return CTypeSpecs::Enum { name: None };
1186                }
1187                // typedef 名
1188                if let Some(name) = interner.lookup(s) {
1189                    CTypeSpecs::TypedefName(name)
1190                } else {
1191                    // 未知の型は typedef 名として扱う(文字列で保持できないので)
1192                    // この場合は interner に登録されていないため、後で解決する必要がある
1193                    CTypeSpecs::Void // フォールバック
1194                }
1195            }
1196        }
1197    }
1198
1199    /// 後方互換: 文字列に変換(デバッグ用)
1200    pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1201        match self {
1202            TypeRepr::CType { specs, derived, .. } => {
1203                let base = specs.to_display_string(interner);
1204                let mut result = base;
1205                for d in derived {
1206                    match d {
1207                        CDerivedType::Pointer { is_const: true, .. } => result.push_str(" *const"),
1208                        CDerivedType::Pointer { .. } => result.push_str(" *"),
1209                        CDerivedType::Array { size: Some(n) } => {
1210                            result.push_str(&format!("[{}]", n));
1211                        }
1212                        CDerivedType::Array { size: None } => result.push_str("[]"),
1213                        CDerivedType::Function { .. } => result.push_str("()"),
1214                    }
1215                }
1216                result
1217            }
1218            TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1219            TypeRepr::Inferred(inferred) => inferred.to_display_string(interner),
1220        }
1221    }
1222
1223    /// Rust コード生成用の型文字列に変換
1224    pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1225        match self {
1226            TypeRepr::CType { specs, derived, .. } => {
1227                let base = specs.to_rust_string(interner);
1228                // ポインタは逆順に適用(Rustの表記に合わせる)
1229                let mut result = base;
1230                for d in derived.iter().rev() {
1231                    // void ポインタの場合は c_void を使用
1232                    if result == "()" && matches!(d, CDerivedType::Pointer { .. } | CDerivedType::Array { .. }) {
1233                        result = "c_void".to_string();
1234                    }
1235                    result = match d {
1236                        CDerivedType::Pointer { is_const: true, .. } => format!("*const {}", result),
1237                        CDerivedType::Pointer { .. } => format!("*mut {}", result),
1238                        CDerivedType::Array { size: Some(n) } => format!("[{}; {}]", result, n),
1239                        CDerivedType::Array { size: None } => format!("*mut {}", result),
1240                        CDerivedType::Function { .. } => format!("/* fn */"),
1241                    };
1242                }
1243                result
1244            }
1245            TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1246            TypeRepr::Inferred(inferred) => inferred.to_rust_string(interner),
1247        }
1248    }
1249}
1250
1251// ============================================================================
1252// `UnifiedType` → `(CTypeSpecs, Vec<CDerivedType>)` 構造的変換
1253// ============================================================================
1254
1255/// `UnifiedType` を C 型表現 (specs + derived) に分解する。
1256/// FnPtr / Verbatim / Unknown は C 表現に落とせないので `None` を返す。
1257fn unified_to_c(
1258    ut: &crate::unified_type::UnifiedType,
1259    interner: &crate::intern::StringInterner,
1260) -> Option<(CTypeSpecs, Vec<CDerivedType>)> {
1261    use crate::unified_type::{UnifiedType as UT, IntSize as UIS};
1262
1263    match ut {
1264        UT::Void => Some((CTypeSpecs::Void, vec![])),
1265        UT::Bool => Some((CTypeSpecs::Bool, vec![])),
1266        UT::Char { signed } => Some((CTypeSpecs::Char { signed: *signed }, vec![])),
1267        UT::Int { signed, size } => {
1268            // UnifiedType::IntSize::Char は C の signed/unsigned char に倒す
1269            // (TypeRepr::IntSize には Char バリアントが無い)
1270            if matches!(size, UIS::Char) {
1271                return Some((CTypeSpecs::Char { signed: Some(*signed) }, vec![]));
1272            }
1273            let target = match size {
1274                UIS::Char => unreachable!(),
1275                UIS::Short => IntSize::Short,
1276                UIS::Int => IntSize::Int,
1277                UIS::Long => IntSize::Long,
1278                UIS::LongLong => IntSize::LongLong,
1279                UIS::Int128 => IntSize::Int128,
1280            };
1281            Some((CTypeSpecs::Int { signed: *signed, size: target }, vec![]))
1282        }
1283        UT::Float => Some((CTypeSpecs::Float, vec![])),
1284        UT::Double => Some((CTypeSpecs::Double { is_long: false }, vec![])),
1285        UT::LongDouble => Some((CTypeSpecs::Double { is_long: true }, vec![])),
1286        UT::Pointer { inner, is_const } => {
1287            let (specs, mut derived) = unified_to_c(inner, interner)?;
1288            // 最も外側の derived として Pointer を追加 (derived 配列は外→内順)
1289            derived.insert(
1290                0,
1291                CDerivedType::Pointer {
1292                    is_const: *is_const,
1293                    is_volatile: false,
1294                    is_restrict: false,
1295                },
1296            );
1297            Some((specs, derived))
1298        }
1299        UT::Array { inner, size } => {
1300            let (specs, mut derived) = unified_to_c(inner, interner)?;
1301            derived.insert(0, CDerivedType::Array { size: *size });
1302            Some((specs, derived))
1303        }
1304        UT::Named(name) => {
1305            let specs = match interner.lookup(name) {
1306                Some(id) => CTypeSpecs::TypedefName(id),
1307                None => CTypeSpecs::UnknownTypedef(name.clone()),
1308            };
1309            Some((specs, vec![]))
1310        }
1311        UT::FnPtr { .. } | UT::Verbatim(_) | UT::Unknown => None,
1312    }
1313}
1314
1315// ============================================================================
1316// 型名抽出メソッド(文字列ラウンドトリップ廃止用)
1317// ============================================================================
1318
1319impl TypeRepr {
1320    /// ポインタ型の参照先の構造体/typedef 名を InternedStr で取得
1321    ///
1322    /// PtrMember (->) の base 型から構造体名を抽出するために使用。
1323    /// 例: `*mut SV` → `Some(SV)`, `XPVHV *` → `Some(XPVHV)`
1324    pub fn pointee_name(&self) -> Option<InternedStr> {
1325        match self {
1326            TypeRepr::CType { specs, derived, .. } => {
1327                if derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. })) {
1328                    specs.type_name()
1329                } else {
1330                    None
1331                }
1332            }
1333            TypeRepr::RustType { repr, .. } => repr.pointee_name(),
1334            TypeRepr::Inferred(inferred) => inferred.resolved_type()?.pointee_name(),
1335        }
1336    }
1337
1338    /// 非ポインタ型の構造体/typedef 名を InternedStr で取得
1339    ///
1340    /// Member (.) の base 型から構造体名を抽出するために使用。
1341    /// 例: `union _xhvnameu` → `Some(_xhvnameu)`, `SV` → `Some(SV)`
1342    pub fn type_name(&self) -> Option<InternedStr> {
1343        match self {
1344            TypeRepr::CType { specs, .. } => specs.type_name(),
1345            TypeRepr::RustType { repr, .. } => repr.type_name(),
1346            TypeRepr::Inferred(inferred) => inferred.resolved_type()?.type_name(),
1347        }
1348    }
1349}
1350
1351impl CTypeSpecs {
1352    /// 構造体/typedef/enum 名を InternedStr で取得
1353    pub fn type_name(&self) -> Option<InternedStr> {
1354        match self {
1355            CTypeSpecs::Struct { name: Some(n), .. } => Some(*n),
1356            CTypeSpecs::TypedefName(n) => Some(*n),
1357            CTypeSpecs::Enum { name: Some(n) } => Some(*n),
1358            _ => None,
1359        }
1360    }
1361}
1362
1363impl InferredType {
1364    /// Inferred ラッパーを解決して内側の TypeRepr を返す
1365    ///
1366    /// 各 InferredType バリアントが保持する「結果の型」を取得する。
1367    /// `pointee_name()` / `type_name()` から再帰的に呼ばれる。
1368    pub fn resolved_type(&self) -> Option<&TypeRepr> {
1369        match self {
1370            InferredType::Cast { target_type } => Some(target_type),
1371            InferredType::PtrMemberAccess { field_type: Some(ft), .. } => Some(ft),
1372            InferredType::MemberAccess { field_type: Some(ft), .. } => Some(ft),
1373            InferredType::ArraySubscript { element_type, .. } => Some(element_type),
1374            InferredType::AddressOf { inner_type } => Some(inner_type),
1375            InferredType::Dereference { pointer_type } => Some(pointer_type),
1376            InferredType::SymbolLookup { resolved_type, .. } => Some(resolved_type),
1377            InferredType::IncDec { inner_type } => Some(inner_type),
1378            InferredType::Assignment { lhs_type } => Some(lhs_type),
1379            InferredType::Comma { rhs_type } => Some(rhs_type),
1380            InferredType::Conditional { result_type, .. } => Some(result_type),
1381            InferredType::BinaryOp { result_type, .. } => Some(result_type),
1382            InferredType::UnaryArithmetic { inner_type } => Some(inner_type),
1383            InferredType::CompoundLiteral { type_name } => Some(type_name),
1384            InferredType::StmtExpr { last_expr_type } => last_expr_type.as_deref(),
1385            _ => None,
1386        }
1387    }
1388}
1389
1390impl RustTypeRepr {
1391    /// ポインタ型の参照先の型名を InternedStr で取得
1392    ///
1393    /// RustTypeRepr は String ベースで型名を格納しているため、
1394    /// InternedStr の取得には interner が必要。当面は None を返す。
1395    fn pointee_name(&self) -> Option<InternedStr> {
1396        // RustTypeRepr は String ベースのため InternedStr を直接取得できない。
1397        // 将来的に RustTypeRepr 自体を InternedStr ベースに改修する際に対応する。
1398        None
1399    }
1400
1401    /// 型名を InternedStr で取得
1402    fn type_name(&self) -> Option<InternedStr> {
1403        None
1404    }
1405}
1406
1407// ============================================================================
1408// Display 実装
1409// ============================================================================
1410
1411impl CTypeSpecs {
1412    /// 表示用文字列に変換
1413    pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1414        match self {
1415            CTypeSpecs::Void => "void".to_string(),
1416            CTypeSpecs::Char { signed: None } => "char".to_string(),
1417            CTypeSpecs::Char { signed: Some(true) } => "signed char".to_string(),
1418            CTypeSpecs::Char { signed: Some(false) } => "unsigned char".to_string(),
1419            CTypeSpecs::Int { signed: true, size: IntSize::Short } => "short".to_string(),
1420            CTypeSpecs::Int { signed: false, size: IntSize::Short } => "unsigned short".to_string(),
1421            CTypeSpecs::Int { signed: true, size: IntSize::Int } => "int".to_string(),
1422            CTypeSpecs::Int { signed: false, size: IntSize::Int } => "unsigned int".to_string(),
1423            CTypeSpecs::Int { signed: true, size: IntSize::Long } => "long".to_string(),
1424            CTypeSpecs::Int { signed: false, size: IntSize::Long } => "unsigned long".to_string(),
1425            CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "long long".to_string(),
1426            CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "unsigned long long".to_string(),
1427            CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "__int128".to_string(),
1428            CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "unsigned __int128".to_string(),
1429            CTypeSpecs::Float => "float".to_string(),
1430            CTypeSpecs::Double { is_long: false } => "double".to_string(),
1431            CTypeSpecs::Double { is_long: true } => "long double".to_string(),
1432            CTypeSpecs::Bool => "_Bool".to_string(),
1433            CTypeSpecs::Struct { name: Some(n), is_union: false } => {
1434                format!("struct {}", interner.get(*n))
1435            }
1436            CTypeSpecs::Struct { name: None, is_union: false } => "struct".to_string(),
1437            CTypeSpecs::Struct { name: Some(n), is_union: true } => {
1438                format!("union {}", interner.get(*n))
1439            }
1440            CTypeSpecs::Struct { name: None, is_union: true } => "union".to_string(),
1441            CTypeSpecs::Enum { name: Some(n) } => format!("enum {}", interner.get(*n)),
1442            CTypeSpecs::Enum { name: None } => "enum".to_string(),
1443            CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1444            CTypeSpecs::UnknownTypedef(s) => s.clone(),
1445        }
1446    }
1447
1448    /// Rust コード生成用の型文字列に変換
1449    pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1450        match self {
1451            CTypeSpecs::Void => "()".to_string(),
1452            CTypeSpecs::Char { signed: None } => "c_char".to_string(),
1453            CTypeSpecs::Char { signed: Some(true) } => "c_schar".to_string(),
1454            CTypeSpecs::Char { signed: Some(false) } => "c_uchar".to_string(),
1455            CTypeSpecs::Int { signed: true, size: IntSize::Short } => "c_short".to_string(),
1456            CTypeSpecs::Int { signed: false, size: IntSize::Short } => "c_ushort".to_string(),
1457            CTypeSpecs::Int { signed: true, size: IntSize::Int } => "c_int".to_string(),
1458            CTypeSpecs::Int { signed: false, size: IntSize::Int } => "c_uint".to_string(),
1459            CTypeSpecs::Int { signed: true, size: IntSize::Long } => "c_long".to_string(),
1460            CTypeSpecs::Int { signed: false, size: IntSize::Long } => "c_ulong".to_string(),
1461            CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "c_longlong".to_string(),
1462            CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "c_ulonglong".to_string(),
1463            CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "i128".to_string(),
1464            CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "u128".to_string(),
1465            CTypeSpecs::Float => "c_float".to_string(),
1466            CTypeSpecs::Double { is_long: false } => "c_double".to_string(),
1467            CTypeSpecs::Double { is_long: true } => "c_double".to_string(), // long double → c_double
1468            CTypeSpecs::Bool => "bool".to_string(),
1469            CTypeSpecs::Struct { name: Some(n), .. } => interner.get(*n).to_string(),
1470            CTypeSpecs::Struct { name: None, .. } => "/* anonymous struct */".to_string(),
1471            CTypeSpecs::Enum { name: Some(n) } => interner.get(*n).to_string(),
1472            CTypeSpecs::Enum { name: None } => "/* anonymous enum */".to_string(),
1473            CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1474            CTypeSpecs::UnknownTypedef(s) => s.clone(),
1475        }
1476    }
1477}
1478
1479impl RustTypeRepr {
1480    /// 最外ポインタの is_const を true に変更する
1481    pub fn make_outer_pointer_const(&mut self) {
1482        if let RustTypeRepr::Pointer { is_const, .. } = self {
1483            *is_const = true;
1484        }
1485    }
1486
1487    /// 最外ポインタを持つかどうか
1488    pub fn has_outer_pointer(&self) -> bool {
1489        matches!(self, RustTypeRepr::Pointer { .. })
1490    }
1491}
1492
1493impl RustTypeRepr {
1494    /// 表示用文字列に変換
1495    pub fn to_display_string(&self) -> String {
1496        match self {
1497            RustTypeRepr::CPrimitive(kind) => kind.to_string(),
1498            RustTypeRepr::RustPrimitive(kind) => kind.to_string(),
1499            RustTypeRepr::Pointer { inner, is_const: true } => {
1500                format!("*const {}", inner.to_display_string())
1501            }
1502            RustTypeRepr::Pointer { inner, is_const: false } => {
1503                format!("*mut {}", inner.to_display_string())
1504            }
1505            RustTypeRepr::Reference { inner, is_mut: true } => {
1506                format!("&mut {}", inner.to_display_string())
1507            }
1508            RustTypeRepr::Reference { inner, is_mut: false } => {
1509                format!("&{}", inner.to_display_string())
1510            }
1511            RustTypeRepr::Named(name) => name.clone(),
1512            RustTypeRepr::Option(inner) => format!("Option<{}>", inner.to_display_string()),
1513            RustTypeRepr::FnPointer { params, ret } => {
1514                let params_str: Vec<_> = params.iter().map(|p| p.to_display_string()).collect();
1515                let ret_str = ret
1516                    .as_ref()
1517                    .map(|r| format!(" -> {}", r.to_display_string()))
1518                    .unwrap_or_default();
1519                format!("fn({}){}", params_str.join(", "), ret_str)
1520            }
1521            RustTypeRepr::Unit => "()".to_string(),
1522            RustTypeRepr::Unknown(s) => s.clone(),
1523        }
1524    }
1525}
1526
1527impl fmt::Display for CPrimitiveKind {
1528    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1529        let s = match self {
1530            CPrimitiveKind::CChar => "c_char",
1531            CPrimitiveKind::CSchar => "c_schar",
1532            CPrimitiveKind::CUchar => "c_uchar",
1533            CPrimitiveKind::CShort => "c_short",
1534            CPrimitiveKind::CUshort => "c_ushort",
1535            CPrimitiveKind::CInt => "c_int",
1536            CPrimitiveKind::CUint => "c_uint",
1537            CPrimitiveKind::CLong => "c_long",
1538            CPrimitiveKind::CUlong => "c_ulong",
1539            CPrimitiveKind::CLongLong => "c_longlong",
1540            CPrimitiveKind::CUlongLong => "c_ulonglong",
1541            CPrimitiveKind::CFloat => "c_float",
1542            CPrimitiveKind::CDouble => "c_double",
1543        };
1544        write!(f, "{}", s)
1545    }
1546}
1547
1548impl fmt::Display for RustPrimitiveKind {
1549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1550        let s = match self {
1551            RustPrimitiveKind::I8 => "i8",
1552            RustPrimitiveKind::I16 => "i16",
1553            RustPrimitiveKind::I32 => "i32",
1554            RustPrimitiveKind::I64 => "i64",
1555            RustPrimitiveKind::I128 => "i128",
1556            RustPrimitiveKind::Isize => "isize",
1557            RustPrimitiveKind::U8 => "u8",
1558            RustPrimitiveKind::U16 => "u16",
1559            RustPrimitiveKind::U32 => "u32",
1560            RustPrimitiveKind::U64 => "u64",
1561            RustPrimitiveKind::U128 => "u128",
1562            RustPrimitiveKind::Usize => "usize",
1563            RustPrimitiveKind::F32 => "f32",
1564            RustPrimitiveKind::F64 => "f64",
1565            RustPrimitiveKind::Bool => "bool",
1566        };
1567        write!(f, "{}", s)
1568    }
1569}
1570
1571impl InferredType {
1572    /// 表示用文字列に変換
1573    pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1574        match self {
1575            InferredType::IntLiteral => "int".to_string(),
1576            InferredType::UIntLiteral => "unsigned int".to_string(),
1577            InferredType::FloatLiteral => "double".to_string(),
1578            InferredType::CharLiteral => "int".to_string(),
1579            InferredType::StringLiteral => "char *".to_string(),
1580            InferredType::SymbolLookup { resolved_type, .. } => {
1581                resolved_type.to_display_string(interner)
1582            }
1583            InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1584            InferredType::BinaryOp { result_type, .. } => result_type.to_display_string(interner),
1585            InferredType::UnaryArithmetic { inner_type } => inner_type.to_display_string(interner),
1586            InferredType::LogicalNot => "int".to_string(),
1587            InferredType::AddressOf { inner_type } => {
1588                format!("{} *", inner_type.to_display_string(interner))
1589            }
1590            InferredType::Dereference { pointer_type } => {
1591                let s = pointer_type.to_display_string(interner);
1592                s.trim_end_matches(" *").to_string()
1593            }
1594            InferredType::IncDec { inner_type } => inner_type.to_display_string(interner),
1595            InferredType::MemberAccess { field_type: Some(ft), .. } => {
1596                ft.to_display_string(interner)
1597            }
1598            InferredType::MemberAccess { base_type, member, .. } => {
1599                format!("{}.{}", base_type, interner.get(*member))
1600            }
1601            InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1602                ft.to_display_string(interner)
1603            }
1604            InferredType::PtrMemberAccess { base_type, member, .. } => {
1605                format!("{}->{}", base_type, interner.get(*member))
1606            }
1607            InferredType::ArraySubscript { element_type, .. } => {
1608                element_type.to_display_string(interner)
1609            }
1610            InferredType::Conditional { result_type, .. } => {
1611                result_type.to_display_string(interner)
1612            }
1613            InferredType::Comma { rhs_type } => rhs_type.to_display_string(interner),
1614            InferredType::Assignment { lhs_type } => lhs_type.to_display_string(interner),
1615            InferredType::Cast { target_type } => target_type.to_display_string(interner),
1616            InferredType::Sizeof | InferredType::Alignof => "unsigned long".to_string(),
1617            InferredType::CompoundLiteral { type_name } => type_name.to_display_string(interner),
1618            InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_display_string(interner),
1619            InferredType::StmtExpr { last_expr_type: None } => "void".to_string(),
1620            InferredType::Assert => "void".to_string(),
1621            InferredType::FunctionReturn { func_name } => {
1622                format!("{}()", interner.get(*func_name))
1623            }
1624        }
1625    }
1626
1627    /// Rust コード生成用の型文字列に変換
1628    pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1629        match self {
1630            InferredType::IntLiteral => "c_int".to_string(),
1631            InferredType::UIntLiteral => "c_uint".to_string(),
1632            InferredType::FloatLiteral => "c_double".to_string(),
1633            InferredType::CharLiteral => "c_int".to_string(),
1634            InferredType::StringLiteral => "*const c_char".to_string(),
1635            InferredType::SymbolLookup { resolved_type, .. } => {
1636                resolved_type.to_rust_string(interner)
1637            }
1638            InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1639            InferredType::BinaryOp { result_type, .. } => result_type.to_rust_string(interner),
1640            InferredType::UnaryArithmetic { inner_type } => inner_type.to_rust_string(interner),
1641            InferredType::LogicalNot => "c_int".to_string(),
1642            InferredType::AddressOf { inner_type } => {
1643                format!("*mut {}", inner_type.to_rust_string(interner))
1644            }
1645            InferredType::Dereference { pointer_type } => {
1646                let s = pointer_type.to_rust_string(interner);
1647                // *mut T → T
1648                s.strip_prefix("*mut ").or_else(|| s.strip_prefix("*const "))
1649                    .unwrap_or(&s).to_string()
1650            }
1651            InferredType::IncDec { inner_type } => inner_type.to_rust_string(interner),
1652            InferredType::MemberAccess { field_type: Some(ft), .. } => {
1653                ft.to_rust_string(interner)
1654            }
1655            InferredType::MemberAccess { base_type, member, .. } => {
1656                format!("/* {}.{} */", base_type, interner.get(*member))
1657            }
1658            InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1659                ft.to_rust_string(interner)
1660            }
1661            InferredType::PtrMemberAccess { base_type, member, .. } => {
1662                format!("/* {}->{} */", base_type, interner.get(*member))
1663            }
1664            InferredType::ArraySubscript { element_type, .. } => {
1665                element_type.to_rust_string(interner)
1666            }
1667            InferredType::Conditional { result_type, .. } => {
1668                result_type.to_rust_string(interner)
1669            }
1670            InferredType::Comma { rhs_type } => rhs_type.to_rust_string(interner),
1671            InferredType::Assignment { lhs_type } => lhs_type.to_rust_string(interner),
1672            InferredType::Cast { target_type } => target_type.to_rust_string(interner),
1673            InferredType::Sizeof | InferredType::Alignof => "c_ulong".to_string(),
1674            InferredType::CompoundLiteral { type_name } => type_name.to_rust_string(interner),
1675            InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_rust_string(interner),
1676            InferredType::StmtExpr { last_expr_type: None } => "()".to_string(),
1677            InferredType::Assert => "()".to_string(),
1678            InferredType::FunctionReturn { func_name } => {
1679                format!("/* {}() ret */", interner.get(*func_name))
1680            }
1681        }
1682    }
1683}
1684
1685// ============================================================================
1686// テスト
1687// ============================================================================
1688
1689#[cfg(test)]
1690mod tests {
1691    use super::*;
1692
1693    fn ptr(is_const: bool) -> CDerivedType {
1694        CDerivedType::Pointer { is_const, is_volatile: false, is_restrict: false }
1695    }
1696
1697    fn const_flags(derived: &[CDerivedType]) -> Vec<bool> {
1698        derived.iter().map(|d| match d {
1699            CDerivedType::Pointer { is_const, .. } => *is_const,
1700            _ => unreachable!(),
1701        }).collect()
1702    }
1703
1704    /// C の指し先 const → Rust *const への位相シフト (GH #15)
1705    #[test]
1706    fn test_shift_pointee_const() {
1707        // const char *  →  *const char (最内 = base const)
1708        let mut d = vec![ptr(false)];
1709        shift_pointee_const(&mut d, true);
1710        assert_eq!(const_flags(&d), vec![true]);
1711
1712        // char *  →  *mut char
1713        let mut d = vec![ptr(false)];
1714        shift_pointee_const(&mut d, false);
1715        assert_eq!(const_flags(&d), vec![false]);
1716
1717        // char * const  →  *mut char (束縛 const は Rust ポインタ型に現れない)
1718        let mut d = vec![ptr(true)];
1719        shift_pointee_const(&mut d, false);
1720        assert_eq!(const_flags(&d), vec![false]);
1721
1722        // const char **  →  *mut *const char (derived は外→内順)
1723        let mut d = vec![ptr(false), ptr(false)];
1724        shift_pointee_const(&mut d, true);
1725        assert_eq!(const_flags(&d), vec![false, true]);
1726
1727        // char * const *  →  *const *mut char
1728        //   (外側ポインタの指し先 = 内側ポインタ自身が const)
1729        let mut d = vec![ptr(false), ptr(true)];
1730        shift_pointee_const(&mut d, false);
1731        assert_eq!(const_flags(&d), vec![true, false]);
1732    }
1733
1734    #[test]
1735    fn test_rust_type_repr_from_string() {
1736        assert!(matches!(
1737            RustTypeRepr::from_type_string("c_int"),
1738            RustTypeRepr::CPrimitive(CPrimitiveKind::CInt)
1739        ));
1740
1741        assert!(matches!(
1742            RustTypeRepr::from_type_string("i32"),
1743            RustTypeRepr::RustPrimitive(RustPrimitiveKind::I32)
1744        ));
1745
1746        assert!(matches!(
1747            RustTypeRepr::from_type_string("()"),
1748            RustTypeRepr::Unit
1749        ));
1750
1751        if let RustTypeRepr::Pointer { inner, is_const: false } =
1752            RustTypeRepr::from_type_string("*mut SV")
1753        {
1754            assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1755        } else {
1756            panic!("Expected *mut SV");
1757        }
1758
1759        if let RustTypeRepr::Pointer { inner, is_const: true } =
1760            RustTypeRepr::from_type_string("*const c_char")
1761        {
1762            assert!(matches!(*inner, RustTypeRepr::CPrimitive(CPrimitiveKind::CChar)));
1763        } else {
1764            panic!("Expected *const c_char");
1765        }
1766    }
1767
1768    #[test]
1769    fn test_rust_type_repr_from_string_with_spaces() {
1770        // syn の出力形式(スペースあり)
1771        if let RustTypeRepr::Pointer { inner, is_const: false } =
1772            RustTypeRepr::from_type_string("* mut SV")
1773        {
1774            assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1775        } else {
1776            panic!("Expected * mut SV");
1777        }
1778    }
1779
1780    #[test]
1781    fn test_c_primitive_display() {
1782        assert_eq!(CPrimitiveKind::CInt.to_string(), "c_int");
1783        assert_eq!(CPrimitiveKind::CUlong.to_string(), "c_ulong");
1784    }
1785
1786    #[test]
1787    fn test_rust_primitive_display() {
1788        assert_eq!(RustPrimitiveKind::I32.to_string(), "i32");
1789        assert_eq!(RustPrimitiveKind::Usize.to_string(), "usize");
1790    }
1791
1792    // === Stage 5: 構造的 pointer 判定 (`is_pointer_type` / `is_void_pointer`) ===
1793
1794    fn make_void_ptr() -> TypeRepr {
1795        TypeRepr::CType {
1796            specs: CTypeSpecs::Void,
1797            derived: vec![CDerivedType::Pointer {
1798                is_const: false,
1799                is_volatile: false,
1800                is_restrict: false,
1801            }],
1802            source: CTypeSource::Apidoc { raw: "void *".to_string() },
1803        }
1804    }
1805
1806    fn make_concrete_ptr() -> TypeRepr {
1807        TypeRepr::CType {
1808            specs: CTypeSpecs::Char { signed: None },
1809            derived: vec![CDerivedType::Pointer {
1810                is_const: false,
1811                is_volatile: false,
1812                is_restrict: false,
1813            }],
1814            source: CTypeSource::Apidoc { raw: "char *".to_string() },
1815        }
1816    }
1817
1818    #[test]
1819    fn test_void_pointer_structural() {
1820        let vp = make_void_ptr();
1821        assert!(vp.is_pointer_type());
1822        assert!(vp.is_void_pointer());
1823        assert!(!vp.is_concrete_pointer());
1824    }
1825
1826    #[test]
1827    fn test_concrete_pointer_structural() {
1828        let cp = make_concrete_ptr();
1829        assert!(cp.is_pointer_type());
1830        assert!(!cp.is_void_pointer());
1831        assert!(cp.is_concrete_pointer());
1832    }
1833
1834    #[test]
1835    fn test_inferred_member_access_pointer_recursive() {
1836        // bindings.rs 経由で MemberAccess の field_type が `*mut c_char` のとき、
1837        // `is_pointer_type` は Inferred を再帰参照して true を返すべき。
1838        // (Stage 4 までの `has_outer_pointer` は false を返してしまう)
1839        let inner = make_concrete_ptr();
1840        let inferred = TypeRepr::Inferred(InferredType::MemberAccess {
1841            base_type: "xpvcv".to_string(),
1842            member: crate::intern::StringInterner::new().intern("foo"),
1843            field_type: Some(Box::new(inner)),
1844        });
1845        assert!(inferred.is_pointer_type());
1846        assert!(!inferred.is_void_pointer());
1847        assert!(inferred.is_concrete_pointer());
1848        // 一方、has_outer_pointer は既存挙動 (Inferred → false) を維持
1849        assert!(!inferred.has_outer_pointer());
1850    }
1851}