Skip to main content

libperl_macrogen/
type_env.rs

1//! 型環境・制約管理モジュール
2//!
3//! マクロ型推論のための型制約を収集・管理する。
4//! ExprId に紐づく型制約を複数ソースから収集し、
5//! 簡約せずにそのまま保持して観察可能にする。
6
7use std::collections::HashMap;
8
9use crate::ast::ExprId;
10use crate::intern::InternedStr;
11use crate::type_repr::TypeRepr;
12
13/// 型制約
14///
15/// 簡約せずにそのまま保持し、デバッグ・観察可能にする。
16/// 出所情報は TypeRepr 内に含まれる。
17#[derive(Debug, Clone)]
18pub struct TypeConstraint {
19    /// 対象となる式の ID
20    pub expr_id: ExprId,
21    /// 構造化された型表現(出所情報を含む)
22    pub ty: TypeRepr,
23    /// デバッグ用コンテキスト(どこで取得したか)
24    pub context: String,
25}
26
27impl TypeConstraint {
28    /// 新しい型制約を作成
29    pub fn new(expr_id: ExprId, ty: TypeRepr, context: impl Into<String>) -> Self {
30        Self {
31            expr_id,
32            ty,
33            context: context.into(),
34        }
35    }
36
37    /// 出所の表示用文字列を取得
38    pub fn source_display(&self) -> &'static str {
39        self.ty.source_display()
40    }
41}
42
43/// ExprId とパラメータ名のリンク情報
44#[derive(Debug, Clone)]
45pub struct ParamLink {
46    /// 式 ID
47    pub expr_id: ExprId,
48    /// パラメータ名
49    pub param_name: InternedStr,
50    /// リンクのコンテキスト
51    pub context: String,
52}
53
54/// 型環境
55///
56/// マクロの型推論に使用する型制約を収集・管理する。
57/// パラメータ、式、戻り値それぞれに対する制約を保持する。
58#[derive(Debug, Clone, Default)]
59pub struct TypeEnv {
60    /// パラメータ名 → 型制約リスト
61    pub param_constraints: HashMap<InternedStr, Vec<TypeConstraint>>,
62
63    /// ExprId → 型制約リスト
64    pub expr_constraints: HashMap<ExprId, Vec<TypeConstraint>>,
65
66    /// 戻り値の型制約
67    pub return_constraints: Vec<TypeConstraint>,
68
69    /// ExprId → パラメータ名のリンク(正引き)
70    pub expr_to_param: Vec<ParamLink>,
71
72    /// パラメータ名 → ExprId リスト(逆引き)
73    ///
74    /// パラメータを参照する全ての式の ExprId を保持。
75    /// 引数の型推論時に、関連する式の型制約を探すために使用。
76    pub param_to_exprs: HashMap<InternedStr, Vec<ExprId>>,
77}
78
79impl TypeEnv {
80    /// 新しい型環境を作成
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    /// パラメータに型制約を追加
86    pub fn add_param_constraint(&mut self, param: InternedStr, constraint: TypeConstraint) {
87        self.param_constraints
88            .entry(param)
89            .or_default()
90            .push(constraint);
91    }
92
93    /// 式に型制約を追加
94    pub fn add_expr_constraint(&mut self, constraint: TypeConstraint) {
95        self.expr_constraints
96            .entry(constraint.expr_id)
97            .or_default()
98            .push(constraint);
99    }
100
101    /// 汎用的な制約追加メソッド
102    pub fn add_constraint(&mut self, constraint: TypeConstraint) {
103        self.add_expr_constraint(constraint);
104    }
105
106    /// 戻り値に型制約を追加
107    pub fn add_return_constraint(&mut self, constraint: TypeConstraint) {
108        self.return_constraints.push(constraint);
109    }
110
111    /// 式をパラメータにリンク
112    ///
113    /// 正引き(expr_to_param)と逆引き(param_to_exprs)の両方を更新する。
114    pub fn link_expr_to_param(&mut self, expr_id: ExprId, param_name: InternedStr, context: impl Into<String>) {
115        // 正引き: ExprId → パラメータ名
116        self.expr_to_param.push(ParamLink {
117            expr_id,
118            param_name,
119            context: context.into(),
120        });
121
122        // 逆引き: パラメータ名 → ExprId リスト
123        self.param_to_exprs
124            .entry(param_name)
125            .or_default()
126            .push(expr_id);
127    }
128
129    /// パラメータの制約を取得
130    pub fn get_param_constraints(&self, param: InternedStr) -> Option<&Vec<TypeConstraint>> {
131        self.param_constraints.get(&param)
132    }
133
134    /// 式の制約を取得
135    pub fn get_expr_constraints(&self, expr_id: ExprId) -> Option<&Vec<TypeConstraint>> {
136        self.expr_constraints.get(&expr_id)
137    }
138
139    /// 式に紐づくパラメータ名を取得
140    pub fn get_linked_param(&self, expr_id: ExprId) -> Option<InternedStr> {
141        self.expr_to_param
142            .iter()
143            .find(|link| link.expr_id == expr_id)
144            .map(|link| link.param_name)
145    }
146
147    /// パラメータ制約の総数
148    pub fn param_constraint_count(&self) -> usize {
149        self.param_constraints.values().map(|v| v.len()).sum()
150    }
151
152    /// 式制約の総数
153    pub fn expr_constraint_count(&self) -> usize {
154        self.expr_constraints.values().map(|v| v.len()).sum()
155    }
156
157    /// 戻り値制約の数
158    pub fn return_constraint_count(&self) -> usize {
159        self.return_constraints.len()
160    }
161
162    /// 戻り値の型を取得(最初の制約から)
163    pub fn get_return_type(&self) -> Option<&TypeRepr> {
164        self.return_constraints.first().map(|c| &c.ty)
165    }
166
167    /// 全制約の総数
168    pub fn total_constraint_count(&self) -> usize {
169        self.param_constraint_count() + self.expr_constraint_count() + self.return_constraint_count()
170    }
171
172    /// 環境が空かどうか
173    pub fn is_empty(&self) -> bool {
174        self.param_constraints.is_empty()
175            && self.expr_constraints.is_empty()
176            && self.return_constraints.is_empty()
177    }
178
179    /// 他の型環境をマージ
180    pub fn merge(&mut self, other: TypeEnv) {
181        for (param, constraints) in other.param_constraints {
182            self.param_constraints
183                .entry(param)
184                .or_default()
185                .extend(constraints);
186        }
187        for (expr_id, constraints) in other.expr_constraints {
188            self.expr_constraints
189                .entry(expr_id)
190                .or_default()
191                .extend(constraints);
192        }
193        self.return_constraints.extend(other.return_constraints);
194        self.expr_to_param.extend(other.expr_to_param);
195
196        // 逆引き辞書もマージ
197        for (param, expr_ids) in other.param_to_exprs {
198            self.param_to_exprs
199                .entry(param)
200                .or_default()
201                .extend(expr_ids);
202        }
203    }
204
205    /// デバッグ用: 制約のサマリを文字列で取得
206    pub fn summary(&self) -> String {
207        format!(
208            "TypeEnv {{ params: {}, exprs: {}, returns: {}, links: {} }}",
209            self.param_constraints.len(),
210            self.expr_constraints.len(),
211            self.return_constraints.len(),
212            self.expr_to_param.len(),
213        )
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220    use crate::intern::StringInterner;
221    use crate::type_repr::{CTypeSource, CTypeSpecs, InferredType, IntSize, RustTypeRepr, RustTypeSource};
222
223    /// テスト用: C ヘッダー由来の int 型を作成
224    fn c_int_type() -> TypeRepr {
225        TypeRepr::CType {
226            specs: CTypeSpecs::Int { signed: true, size: IntSize::Int },
227            derived: vec![],
228            source: CTypeSource::Header,
229        }
230    }
231
232    /// テスト用: Rust bindings 由来の c_int 型を作成
233    fn rust_c_int_type() -> TypeRepr {
234        TypeRepr::RustType {
235            repr: RustTypeRepr::from_type_string("c_int"),
236            source: RustTypeSource::FnParam { func_name: "test".to_string(), param_index: 0 },
237        }
238    }
239
240    /// テスト用: Apidoc 由来の SV * 型を作成
241    fn apidoc_sv_ptr_type() -> TypeRepr {
242        let interner = StringInterner::new();
243        TypeRepr::from_apidoc_string("SV *", &interner)
244    }
245
246    #[test]
247    fn test_type_env_new() {
248        let env = TypeEnv::new();
249        assert!(env.is_empty());
250        assert_eq!(env.total_constraint_count(), 0);
251    }
252
253    #[test]
254    fn test_add_expr_constraint() {
255        let mut env = TypeEnv::new();
256        let expr_id = ExprId::next();
257
258        let constraint = TypeConstraint::new(
259            expr_id,
260            c_int_type(),
261            "test context",
262        );
263
264        env.add_expr_constraint(constraint);
265
266        assert!(!env.is_empty());
267        assert_eq!(env.expr_constraint_count(), 1);
268        assert_eq!(env.get_expr_constraints(expr_id).unwrap().len(), 1);
269    }
270
271    #[test]
272    fn test_add_multiple_constraints() {
273        let mut env = TypeEnv::new();
274        let expr_id = ExprId::next();
275
276        // 同じ式に複数の制約
277        env.add_constraint(TypeConstraint::new(
278            expr_id,
279            c_int_type(),
280            "from C header",
281        ));
282        env.add_constraint(TypeConstraint::new(
283            expr_id,
284            rust_c_int_type(),
285            "from bindings",
286        ));
287
288        let constraints = env.get_expr_constraints(expr_id).unwrap();
289        assert_eq!(constraints.len(), 2);
290        assert_eq!(constraints[0].source_display(), "c-header");
291        assert_eq!(constraints[1].source_display(), "rust-bindings");
292    }
293
294    #[test]
295    fn test_link_expr_to_param() {
296        let mut env = TypeEnv::new();
297        let expr_id = ExprId::next();
298
299        // パラメータ名を StringInterner で作成
300        let mut interner = StringInterner::new();
301        let param_name = interner.intern("x");
302
303        env.link_expr_to_param(expr_id, param_name, "parameter reference");
304
305        assert_eq!(env.get_linked_param(expr_id), Some(param_name));
306    }
307
308    #[test]
309    fn test_merge() {
310        let mut env1 = TypeEnv::new();
311        let mut env2 = TypeEnv::new();
312
313        let expr1 = ExprId::next();
314        let expr2 = ExprId::next();
315
316        env1.add_constraint(TypeConstraint::new(
317            expr1,
318            c_int_type(),
319            "env1",
320        ));
321
322        env2.add_constraint(TypeConstraint::new(
323            expr2,
324            TypeRepr::CType {
325                specs: CTypeSpecs::Char { signed: None },
326                derived: vec![],
327                source: CTypeSource::Apidoc { raw: "char".to_string() },
328            },
329            "env2",
330        ));
331
332        env1.merge(env2);
333
334        assert_eq!(env1.expr_constraint_count(), 2);
335        assert!(env1.get_expr_constraints(expr1).is_some());
336        assert!(env1.get_expr_constraints(expr2).is_some());
337    }
338
339    #[test]
340    fn test_return_constraints() {
341        let mut env = TypeEnv::new();
342        let expr_id = ExprId::next();
343
344        env.add_return_constraint(TypeConstraint::new(
345            expr_id,
346            apidoc_sv_ptr_type(),
347            "return type from apidoc",
348        ));
349
350        assert_eq!(env.return_constraint_count(), 1);
351        assert_eq!(env.return_constraints[0].source_display(), "apidoc");
352    }
353
354    #[test]
355    fn test_type_repr_source_display() {
356        // CType sources
357        assert_eq!(c_int_type().source_display(), "c-header");
358        assert_eq!(apidoc_sv_ptr_type().source_display(), "apidoc");
359
360        // RustType
361        assert_eq!(rust_c_int_type().source_display(), "rust-bindings");
362
363        // Inferred
364        let inferred = TypeRepr::Inferred(InferredType::IntLiteral);
365        assert_eq!(inferred.source_display(), "inferred");
366    }
367}