型シノニム (Dtype) を消去し、参照箇所で本体の型に展開する - #27
Open
cedretaber wants to merge 6 commits into
Open
Conversation
Java has no type-alias feature, so Dtype declarations are recorded in an alias table and print nothing, and every type entering the printer from a declaration (Dterm, Dfix, Dind constructor fields) has alias references expanded to their bodies. Custom type extractions are rejected explicitly. Expansion is deliberately independent of the Extraction TypeExpand flag: in Java it is a correctness requirement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MLcons and MLcase carry an ml_type annotation whose type arguments may mention aliases (e.g. list natop); those arguments flow into constructor field types via type_subst_list and would otherwise surface as casts to undeclared Java types or as redundant receiver casts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers a monomorphic alias, a function-type alias (no receiver cast after expansion), a parameterized alias, an alias of an alias, an alias as a constructor field type, an alias as a type argument in MLcons/MLcase annotations, and an unrealized axiom type printed as Object. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With Extraction TypeExpand unset, upstream extraction keeps aliases in MLcons/MLcase type annotations, so java.ml's own expansion gates are the only thing standing between an alias and the generated Java. The existing type_alias case runs under the default flag and never exercises them; the new Driver-less type_alias_noexpand case pins that path with a golden. Also exercise unbox's Nought branch at runtime in DriverTypeAlias. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extract Constant on a type registers an opaque target-language string, and Java cannot declare a type alias for the name to refer to, so the string itself is the type: a custom Dtype declaration prints nothing and every type position prints the custom string (inlined or not — the distinction only matters where a declaration exists). Term-level customs were already honored this way; whether the string is valid Java is the user's responsibility, as in the OCaml backend. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The box inductive generated a nested interface box and a nested class Box, whose java_type_alias$box.class and java_type_alias$Box.class collide on macOS's case-insensitive APFS: one overwrites the other and the class loader then fails with NoClassDefFoundError (wrong name). Renaming the constructor keeps the test's coverage (alias as a constructor field type) while making the generated class files distinct on every CI platform. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #26 (#14 の 6 番目「Dtype を実装する」)
概要
型シノニム(
Definition natlist := list natのような型を返す Definition)は miniml 上でDtype宣言として抽出されますが、Java バックエンドはスタブ(裸のtypeトークンを出力)のままでした。参照側も Java に存在しないシノニム名をそのまま印字するため、シノニムを含むコードは javac を通りませんでした。Java には型エイリアス機能が無く、ラッパークラスで名前を残すとランタイム表現が変わってしまうため、本 PR は 宣言を消去し、参照箇所でシノニムを本体の型に展開する 方針を採ります。失われるのはシグネチャ上の名前のみで、ランタイム表現は不変です(型変数の Object 消去で既により多くの情報が落ちているため、追加の損失は小さい)。
変更点
plugins/extraction/java.ml)pp_declがDtypeを見た時点で表(Constant→ 正規化済み本体)に登録し、宣言は何も出力しないexpand_aliasesがTglob(alias, args)をtype_subst_listによる引数代入付きで再帰展開。Mlutil.type_expandはUnset Extraction TypeExpandで恒等関数になるため流用せず、フラグ非依存の自前実装にしています(Java では展開が正しさの要件のため)ml_typeが AST から printer に入る全箇所Dterm/Dfixの型、Dindのコンストラクタフィールド型MLcons/MLcaseの型注釈(デフォルトのExtraction TypeExpandでは上流で展開済みですが、Unset時はここが唯一の防壁になります)arrows_upto)は無変更ですAxiom t : Type(本体がTaxiom)→Object扱いで登録(項レベル公理の扱いと整合)Tdummy)→ そのまま(Objectに印字される)Extract Constant t => "...")のサポートml_typeの本体が無く、Java ではエイリアス宣言も書けないため、文字列そのものを展開結果と見なし、宣言は出力せず全型位置でカスタム文字列を印字します(pp_type_global)テスト
type_alias(14 ケース目): 単相シノニム / 関数型シノニム(展開後は arrow が見えるため冗長な receiver cast が出ない)/ パラメータ付き / シノニムのシノニム / inductive のフィールド型 /MLcons・MLcase型注釈中の型引数 / 公理型、の 7 形をカバー。Driver で代表値をランタイム検証type_alias_noexpand(15 ケース目):Unset Extraction TypeExpand下でも出力が正しいことを golden で固定(式レベル関門の回帰保護)type_custom(16 ケース目):Extract Constant big => "java.math.BigInteger"と項のカスタム抽出を組み合わせ、シノニム → カスタム型の連鎖も含めて golden + javac + ランタイム検証(BigIntegerの実演算)抽出例(修正前 → 修正後):
カスタム型の抽出例:
実装中に見つけた既存の問題(本 PR のスコープ外)
エイリアスとは無関係の既存バグを 3 件確認しました。テストはこれらを意図的に回避する形にしてあります(回避理由は
.vのコメントに記載):Object型フィールドの引数位置に直接出ると、functional interface へのキャストが無く javac が通らない(例:Cons S Nil)ClassCastExceptionになる'(例:inc2')が Java 識別子にそのまま出力される(ローカル変数はpr_idが$に置換するが、トップレベル名はCommon.pp_global経由で素通し)必要でしたら別 issue として起票します。
🤖 Generated with Claude Code