Skip to content

Check pattern conformance in all MLcase branches (#6)#11

Merged
cedretaber merged 2 commits into
java_extractionfrom
feat/java-case-exhaustiveness
Jul 15, 2026
Merged

Check pattern conformance in all MLcase branches (#6)#11
cedretaber merged 2 commits into
java_extractionfrom
feat/java-case-exhaustiveness

Conversation

@cedretaber

Copy link
Copy Markdown

概要

issue #6 の優先度「高」タスクのひとつ:

MLcase の全分岐でパターン適合性を検査する

  • 最終分岐も無条件キャストしない
  • どの分岐にも適合しない場合は明示的な例外を生成する
  • 非網羅パターンで偶発的な ClassCastException を発生させない

変更内容

plugins/extraction/java.ml

  • 全コンストラクタ分岐(最終分岐を含む)に instanceof 検査を付与。 従来は最終分岐だけコメント + 無条件キャストだったため、どのコンストラクタにも適合しない値で偶発的な ClassCastException が発生していた。
  • チェーン末尾に default を配置:
    • catch-all 分岐(Pwild / Prel)があればそれが default(以降の分岐は到達不能として打ち切り)
    • なければ error("non-exhaustive match") を生成
  • class Main に失敗ヘルパーを追加: static <A> A error(String msg) { throw new RuntimeException(msg); } — Java の throw は文であり三項演算子チェーン内に書けないため。将来 MLexn / MLaxiom の対応(優先度「中」)でも流用可能な名前にしてある。
  • catch-all パターンの構造的な処理(pp_catch_all_pat): Pwild は body をそのまま、Prellet(scrutinee, x -> body) で束縛。従来の Prel は壊れた出力(((x)scrut).x0 相当)になり得た。
  • Ptuple は不正な Java 文字列 "not implemented pattern..." を出す代わりに明示的な抽出エラー(user_err)を投げる。

生成コードの変化(list_reverse の例):

reverse = l ->
(l instanceof Nil) ? new Nil()
: (l instanceof Cons) ? let(((Cons)l).Cons0, x -> ...)
: error("non-exhaustive match")
;

テスト

  • 既存 2 ケース(basic_arithmetic / list_reverse)の golden を再生成。driver は引き続き PASS。
  • 新ケース match_failure を追加(issue のテスト項目「非網羅 case の動作をテストする」に対応): 3 コンストラクタの color 型と rotate 関数を抽出し、driver が
    1. 正常系 3 パターンの結果を検証
    2. new Main.color() {}(どのコンストラクタでもない匿名クラス値)を渡すと、ClassCastException ではなく素の RuntimeException("non-exhaustive match") が飛ぶことを検証(例外クラスの完全一致 + メッセージの両方をチェック)

検証

  • sh test-suite/misc/java-extraction.sh で全 3 ケース PASS(抽出 → golden diff → javac → driver 実行)をローカル確認済み

Closes する項目: issue #6 のチェックリスト「MLcase の全分岐でパターン適合性を検査する」

🤖 Generated with Claude Code

Every constructor branch, including the last one, is now guarded by an
instanceof test instead of casting the final branch unconditionally.
When no branch matches, the generated code calls a new error() helper in
class Main that throws an explicit RuntimeException("non-exhaustive
match"), so a value fitting no constructor no longer surfaces as an
accidental ClassCastException.

Top-level catch-all patterns are handled structurally: Pwild emits its
body as the default of the conditional chain, Prel binds the scrutinee
via let before the body, and Ptuple raises an explicit extraction error
instead of printing invalid Java.

Adds the match_failure test case, whose driver feeds an anonymous
subclass of the extracted interface into a match and asserts that the
failure is the explicit RuntimeException, not a ClassCastException.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment on lines +216 to +221
let rec split acc = function
| [] -> List.rev acc, None
| b :: _ when is_catch_all b -> List.rev acc, Some b (* later branches are unreachable *)
| b :: rest -> split (b :: acc) rest
in
let tests, default = split [] (Array.to_list pv) in

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自前で split 関数を定義し、「検査のある節」と「デフォルト・フォールバック節」に分けている。
Pwild, Prel 以降は到達しないので無視している。

str "class Main {" ++ fnl() ++ fnl() ++
str "static <A, B> B let(A val, Function<A, B> cont) { return cont.apply(val); }" ++
fnl() ++ fnl() ++
str "static <A> A error(String msg) { throw new RuntimeException(msg); }" ++

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

分かりやすいエラーを投げる為のヘルパー。

let tests, default = split [] (Array.to_list pv) in
let pp_default = match default with
| Some b -> pp_catch_all_pat table env scrut b
| None -> str "error(\"non-exhaustive match\")"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

パターンマッチの網羅性は Rocq 側で担保しているので、ここには基本的に来ないかも。
ただし、 Java 側の定義を呼び出す FFI を利用するとか、 magic を使うとかしていた場合、あり得るかも?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

来ないけどコード上は一応生成している 📝

@cedretaber cedretaber self-assigned this Jul 8, 2026
Self-review found that no test exercised the catch-all code paths: every
existing golden match is fully constructor-enumerated, so pp_catch_all_pat
was never executed. The new case covers both shapes: a Pwild default
(shared branch body not using the matched value) and a Prel default (a
compound scrutinee bound to a variable used by the branch body). The
driver also documents that a catch-all absorbs values beyond the
extracted constructors instead of raising a match failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cedretaber
cedretaber marked this pull request as ready for review July 8, 2026 13:57
@cedretaber
cedretaber requested a review from hiroshi-cl July 9, 2026 11:45
| Ptuple l -> "not implemented pattern...", []
| Pwild -> "_", [] (* TODO *)
| Prel n -> Id.to_string (get_db_name n env), []
| Ptuple _ -> user_err Pp.(str "Cannot handle tuple patterns in Java yet.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちょっと親切

Comment on lines +8 to +9
"$self_dir/java-extraction/run-case.sh" match_failure
"$self_dir/java-extraction/run-case.sh" match_default

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test :yoshi:

| Pwild -> "_", [] (* TODO *)
| Prel n -> Id.to_string (get_db_name n env), []
| Ptuple _ -> user_err Pp.(str "Cannot handle tuple patterns in Java yet.")
| Pwild | Prel _ -> assert false (* catch-all: handled in pp_pat_branches *)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#6 (comment)
によると通常発生しないようなのでよさそうか

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ですです、ここに来る場合はロジックが間違っている。

@cedretaber
cedretaber merged commit 3d902d6 into java_extraction Jul 15, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants