diff --git a/.github/Dockerfile b/.github/Dockerfile index 28ce8fd5..fb389090 100644 --- a/.github/Dockerfile +++ b/.github/Dockerfile @@ -6,5 +6,8 @@ ENV OPAMJOBS=$NCPUS RUN opam repo add coq-released https://coq.inria.fr/opam/released \ && opam update \ - && opam install coq-equations coq-menhirlib menhir ppx_inline_test \ + && opam install coq-equations coq-menhirlib menhir ppx_expect \ && eval $(opam env) + +RUN sudo apt-get update -y -q \ + && DEBIAN_FRONTEND=noninteractive sudo apt-get install -y -q --no-install-recommends pandoc diff --git a/.github/workflows/ci_build.yaml b/.github/workflows/ci_build.yaml index 7443cef9..b53c0407 100644 --- a/.github/workflows/ci_build.yaml +++ b/.github/workflows/ci_build.yaml @@ -3,9 +3,11 @@ on: push: branches: - main + - ext/** pull_request: branches: - main + - ext/** workflow_dispatch: permissions: @@ -71,12 +73,14 @@ jobs: endGroup script: | startGroup "Build binary" + make pretty-timed if [[ "${{ env.DOC_DEPLOY }}" == 'true' ]]; then make coqdoc - else - make fi endGroup + startGroup "Run inline tests" + dune runtest + endGroup after_script: | startGroup "after" endGroup diff --git a/.gitignore b/.gitignore index b3928d05..3b9fdc1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ _build +*.opam # Ignore generated parser code **/parser.ml diff --git a/.ocamlformat b/.ocamlformat index 42476c94..4b37f756 100644 --- a/.ocamlformat +++ b/.ocamlformat @@ -1,2 +1,8 @@ profile = default -version = 0.24.1 +version = 0.26.2 + +break-fun-decl = fit-or-vertical +break-fun-sig = fit-or-vertical +cases-exp-indent = 3 +exp-grouping = preserve +if-then-else = keyword-first diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0014b174 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 the CompLogic group, Mcgill University + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile index a690d785..f91fb122 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,14 @@ -.PHONY: all clean +.PHONY: all pretty-timed test coqdoc clean all: @$(MAKE) -C theories @dune build -coqdoc: all +pretty-timed: + @$(MAKE) pretty-timed -C theories + @dune build + +coqdoc: @${MAKE} coqdoc -C theories clean: diff --git a/README.md b/README.md index c2c90121..931ff3e2 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,10 @@ possible. Once `make` finishes, you can run the binary: ``` +dune exec mcltt examples/nary.mcl # or your own example +``` +or more directly +``` _build/default/driver/mcltt.exe examples/nary.mcl # or your own example ``` @@ -88,3 +92,10 @@ cd theories make ``` +## Branches + +The Github repo includes the following special branches: + +1. `main`: the main branch that is used to generate this homepage and Coqdoc; +2. `ext/*`: branches in this pattern are variations of `main` that implements various extensions. They are often used to implement extensions that require non-trivial workload and are aimed to be merged to `main` eventually; +3. `gh-pages`: the branch to host the homepage. diff --git a/assets/extra/header.html b/assets/extra/header.html index 98ff925f..3249b6cf 100644 --- a/assets/extra/header.html +++ b/assets/extra/header.html @@ -18,7 +18,7 @@ - Project Page + Project Page Index Table of Contents diff --git a/driver/Lexer.mli b/driver/Lexer.mli new file mode 100644 index 00000000..c3a2e915 --- /dev/null +++ b/driver/Lexer.mli @@ -0,0 +1,7 @@ +val format_position : Format.formatter -> Lexing.position -> unit +val format_range : Format.formatter -> Lexing.position * Lexing.position -> unit +val format_token : Format.formatter -> MclttExtracted.Parser.token -> unit +val read : Lexing.lexbuf -> MclttExtracted.Parser.token + +val lexbuf_to_token_buffer : + Lexing.lexbuf -> MclttExtracted.Parser.MenhirLibParser.Inter.buffer diff --git a/driver/Lexer.mll b/driver/Lexer.mll index cd92ae95..b590befb 100644 --- a/driver/Lexer.mll +++ b/driver/Lexer.mll @@ -104,3 +104,14 @@ and comment = parse | "*)" { read lexbuf } | _ { comment lexbuf } + +{ + let rec lexbuf_to_token_buffer lexbuf = + lazy + begin + try + MenhirLibParser.Inter.Buf_cons (read lexbuf, lexbuf_to_token_buffer lexbuf) + with + | Failure s -> prerr_string s; raise Exit + end +} diff --git a/driver/Main.ml b/driver/Main.ml index 5b847973..0859dbb5 100644 --- a/driver/Main.ml +++ b/driver/Main.ml @@ -4,26 +4,23 @@ open Parser open MenhirLibParser.Inter open Entrypoint -let main ?(default_fp = "") () = - let fp = - if default_fp <> "" - then default_fp - else - begin - print_string "File path to load: "; - read_line () - end - in - let chan = open_in fp in - (* Before parsing, we must generate a token stream from a lexer buffer, - which we then feed into the parser. *) - let rec loop lexbuf = - lazy ( - try - Buf_cons (Lexer.read lexbuf, loop lexbuf) - with - | Failure s -> prerr_string s; raise Exit) in - let token_stream = loop (Lexing.from_channel chan) in - let res = Entrypoint.main 500 token_stream in - Format.printf "%a@." - PrettyPrinter.format_main_result res +let get_exit_code result : int = + match result with + | AllGood _ -> 0 + | TypeCheckingFailure _ -> 1 + | ElaborationFailure _ -> 2 + | ParserFailure _ -> 3 + | ParserTimeout _ -> 4 + +let main_of_lexbuf lexbuf = + Lexer.lexbuf_to_token_buffer lexbuf + (* Here, the integer argument is a *log* version of fuel. + Thus, 500 means 2^500. *) + |> Entrypoint.main 500 + |> fun r -> Format.printf "%a@." PrettyPrinter.format_main_result r; get_exit_code r + +let main_of_filename filename = + Lexing.from_channel (open_in filename) |> main_of_lexbuf + +let main_of_program_string program = + Lexing.from_string program |> main_of_lexbuf diff --git a/driver/Main.mli b/driver/Main.mli new file mode 100644 index 00000000..44046b52 --- /dev/null +++ b/driver/Main.mli @@ -0,0 +1,2 @@ +val main_of_filename : string -> int +val main_of_program_string : string -> int diff --git a/driver/Mcltt.ml b/driver/Mcltt.ml index 816004bf..b3ebaa50 100644 --- a/driver/Mcltt.ml +++ b/driver/Mcltt.ml @@ -1,10 +1,12 @@ open Mcltt.Main let () = - if Array.length Sys.argv <> 2 then - begin - Printf.fprintf stderr "incorrect input: %s \n" Sys.argv.(0); - exit 1 - end; + if Array.length Sys.argv <> 2 + then begin + Printf.fprintf stderr + "Missing argument.\nUsage: %s \n" Sys.argv.(0); + exit 5 + end; let filename = Sys.argv.(1) in - main ~default_fp:filename () + let code = main_of_filename filename in + exit code diff --git a/driver/PrettyPrinter.ml b/driver/PrettyPrinter.ml index dcb3f873..5ffdb518 100644 --- a/driver/PrettyPrinter.ml +++ b/driver/PrettyPrinter.ml @@ -1,6 +1,5 @@ open MclttExtracted.Entrypoint open MclttExtracted.Syntax - module Parser = MclttExtracted.Parser module ParserMessages = MclttExtracted.ParserMessages @@ -8,72 +7,68 @@ module ParserMessages = MclttExtracted.ParserMessages (* Formatting helpers *) (************************************************************) -let pp_print_paren_if (cond: bool) (body: Format.formatter -> unit -> unit) (f: Format.formatter) (): unit = - (if cond then Format.pp_print_char f '('); +let pp_print_paren_if + (cond : bool) + (body : Format.formatter -> unit -> unit) + (f : Format.formatter) + () : unit = + if cond then Format.pp_print_char f '('; body f (); - (if cond then Format.pp_print_char f ')') + if cond then Format.pp_print_char f ')' (************************************************************) (* Formatting Cst.obj *) (************************************************************) -let rec get_nat_of_obj: Cst.obj -> int option = - function - | Cst.Coq_zero -> Some 0 - | Cst.Coq_succ e -> Option.map ((+) 1) (get_nat_of_obj e) - | _ -> None +let rec get_nat_of_obj : Cst.obj -> int option = function + | Cst.Coq_zero -> Some 0 + | Cst.Coq_succ e -> Option.map (( + ) 1) (get_nat_of_obj e) + | _ -> None -let rec get_fn_params_of_obj: Cst.obj -> (string * Cst.obj) list * Cst.obj = +let rec get_fn_params_of_obj : Cst.obj -> (string * Cst.obj) list * Cst.obj = function | Cst.Coq_fn (px, ep, ebody) -> let params, ebody' = get_fn_params_of_obj ebody in ((px, ep) :: params, ebody') - | ebody -> ([], ebody) + | ebody -> ([], ebody) -let rec get_pi_params_of_obj: Cst.obj -> (string * Cst.obj) list * Cst.obj = +let rec get_pi_params_of_obj : Cst.obj -> (string * Cst.obj) list * Cst.obj = function | Cst.Coq_pi (px, ep, eret) -> let params, eret' = get_pi_params_of_obj eret in ((px, ep) :: params, eret') - | eret -> ([], eret) + | eret -> ([], eret) -let rec format_obj_prec (p: int) (f: Format.formatter): Cst.obj -> unit = +let rec format_obj_prec (p : int) (f : Format.formatter) : Cst.obj -> unit = let open Format in function - | Cst.Coq_typ i -> fprintf f "Type@%d" i - | Cst.Coq_nat -> fprintf f "Nat" - | Cst.Coq_zero -> fprintf f "0" - | Cst.Coq_succ e -> - begin - match get_nat_of_obj e with - | Some n -> fprintf f "%d" (1 + n) - | None -> - let impl f () = fprintf f "succ@ %a" (format_obj_prec 2) e in - pp_open_hovbox f 2; - pp_print_paren_if (p >= 2) impl f (); - pp_close_box f () - end + | Cst.Coq_typ i -> fprintf f "Type@%d" i + | Cst.Coq_nat -> fprintf f "Nat" + | Cst.Coq_zero -> fprintf f "0" + | Cst.Coq_succ e -> begin + match get_nat_of_obj e with + | Some n -> fprintf f "%d" (1 + n) + | None -> + let impl f () = fprintf f "succ@ %a" (format_obj_prec 2) e in + pp_open_hovbox f 2; + pp_print_paren_if (p >= 2) impl f (); + pp_close_box f () + end | Cst.Coq_natrec (escr, mx, em, ez, sx, sr, es) -> let impl f () = - fprintf f "@[@[rec %a@ return %s -> %a@]@ @[| zero =>@ %a@]@ @[| succ %s, %s =>@ %a@]@ end@]" - format_obj escr - mx - format_obj em - format_obj ez - sx - sr - format_obj es + fprintf f + "@[@[rec %a@ return %s -> %a@]@ @[| zero =>@ \ + %a@]@ @[| succ %s, %s =>@ %a@]@ end@]" + format_obj escr mx format_obj em format_obj ez sx sr format_obj es in pp_print_paren_if (p >= 1) impl f () - | Cst.Coq_app (ef, ea) -> + | Cst.Coq_app (ef, ea) -> let impl f () = - fprintf f "%a@ %a" - (format_obj_prec 1) ef - (format_obj_prec 2) ea; + fprintf f "%a@ %a" (format_obj_prec 1) ef (format_obj_prec 2) ea in pp_open_hvbox f 2; pp_print_paren_if (p >= 2) impl f (); pp_close_box f () - | Cst.Coq_fn (px, ep, ebody) -> + | Cst.Coq_fn (px, ep, ebody) -> let params, ebody' = get_fn_params_of_obj ebody in let impl f () = pp_print_string f "fun "; @@ -86,13 +81,12 @@ let rec format_obj_prec (p: int) (f: Format.formatter): Cst.obj -> unit = then pp_print_space f () else pp_force_newline f () end; - fprintf f "-> @[%a@]" - format_obj ebody' + fprintf f "-> @[%a@]" format_obj ebody' in pp_open_hvbox f 2; pp_print_paren_if (p >= 1) impl f (); pp_close_box f () - | Cst.Coq_pi (px, ep, eret) -> + | Cst.Coq_pi (px, ep, eret) -> let params, eret' = get_pi_params_of_obj eret in let impl f () = pp_print_string f "forall "; @@ -105,65 +99,70 @@ let rec format_obj_prec (p: int) (f: Format.formatter): Cst.obj -> unit = then pp_print_space f () else pp_force_newline f () end; - fprintf f "-> @[%a@]" - format_obj eret' + fprintf f "-> @[%a@]" format_obj eret' in pp_open_hvbox f 2; pp_print_paren_if (p >= 1) impl f (); pp_close_box f () - | Cst.Coq_var x -> pp_print_string f x -and format_obj_param f (px, ep) = - Format.fprintf f "(%s : %a)" - px - format_obj ep + | Cst.Coq_var x -> pp_print_string f x + +and format_obj_param f (px, ep) = Format.fprintf f "(%s : %a)" px format_obj ep and format_obj f = format_obj_prec 0 f (************************************************************) (* Formatting exp *) (************************************************************) let exp_to_obj = - let new_var = + let new_var, reset_var_suffix = let suffix = ref 0 in - fun () -> - incr suffix; - "x" ^ string_of_int !suffix + ( (fun () -> + incr suffix; + "x" ^ string_of_int !suffix), + fun () -> suffix := 0 ) in - let new_tyvar = + let new_tyvar, reset_tyvar_suffix = let suffix = ref 0 in - fun () -> - incr suffix; - "A" ^ string_of_int !suffix + ( (fun () -> + incr suffix; + "A" ^ string_of_int !suffix), + fun () -> suffix := 0 ) in - let rec impl (ctx: string list): exp -> Cst.obj = - function + let rec impl (ctx : string list) : exp -> Cst.obj = function | Coq_a_zero -> Cst.Coq_zero | Coq_a_succ e -> Cst.Coq_succ (impl ctx e) | Coq_a_natrec (em, ez, es, escr) -> - let mx, sx, sr = new_var (), new_var (), new_var () in - Cst.Coq_natrec (impl ctx escr, mx, impl (mx :: ctx) em, impl ctx ez, sx, sr, impl (sr :: sx :: ctx) es) + let mx = new_var () in + let sx = new_var () in + let sr = match em with Coq_a_typ _ -> new_tyvar () | _ -> new_var () in + let escr' = impl ctx escr in + let em' = impl (mx :: ctx) em in + let ez' = impl ctx ez in + let es' = impl (sr :: sx :: ctx) es in + Cst.Coq_natrec (escr', mx, em', ez', sx, sr, es') | Coq_a_nat -> Cst.Coq_nat | Coq_a_typ i -> Cst.Coq_typ i | Coq_a_var x -> Cst.Coq_var (List.nth ctx x) | Coq_a_fn (ep, ebody) -> - let px = - match ep with - | Coq_a_typ _ -> new_tyvar () - | _ -> new_var () - in - Cst.Coq_fn (px, impl ctx ep, impl (px :: ctx) ebody) + let px = match ep with Coq_a_typ _ -> new_tyvar () | _ -> new_var () in + let ep' = impl ctx ep in + let ebody' = impl (px :: ctx) ebody in + Cst.Coq_fn (px, ep', ebody') | Coq_a_app (ef, ea) -> - Cst.Coq_app (impl ctx ef, impl ctx ea) + let ef' = impl ctx ef in + let ea' = impl ctx ea in + Cst.Coq_app (ef', ea') | Coq_a_pi (ep, eret) -> - let px = - match ep with - | Coq_a_typ _ -> new_tyvar () - | _ -> new_var () - in - Cst.Coq_pi (px, impl ctx ep, impl (px :: ctx) eret) + let px = match ep with Coq_a_typ _ -> new_tyvar () | _ -> new_var () in + let ep' = impl ctx ep in + let eret' = impl (px :: ctx) eret in + Cst.Coq_pi (px, ep', eret') | Coq_a_sub _ -> failwith "Invalid internal language construct" in - impl [] - + fun exp -> + reset_var_suffix (); + reset_tyvar_suffix (); + impl [] exp + let format_exp f exp = format_obj f (exp_to_obj exp) (************************************************************) @@ -176,32 +175,26 @@ let format_nf f nf = format_exp f (nf_to_exp nf) (* Formatting main_result *) (************************************************************) -let format_main_result (f: Format.formatter): main_result -> unit = +let format_main_result (f : Format.formatter) : main_result -> unit = let open Format in function | AllGood (cst_typ, cst_exp, typ, exp, nf) -> - fprintf f "@[Parsed:@ @[%a@ : %a@]@]" - format_obj cst_exp + fprintf f "@[Parsed:@ @[%a@ : %a@]@]" format_obj cst_exp format_obj cst_typ; pp_force_newline f (); - fprintf f "@[Elaborated:@ @[%a@ : %a@]@]" - format_exp exp + fprintf f "@[Elaborated:@ @[%a@ : %a@]@]" format_exp exp format_exp typ; pp_force_newline f (); - fprintf f "@[Normalized Result:@ @[%a@ : %a@]@]" - format_nf nf + fprintf f "@[Normalized Result:@ @[%a@ : %a@]@]" format_nf nf format_exp typ | TypeCheckingFailure (typ, exp) -> printf "@[Type Checking Failure:@ %a@;<1 -2>is not of@ %a@]" - format_exp exp - format_exp typ + format_exp exp format_exp typ | ElaborationFailure cst_exp -> printf "@[Elaboration Failure:@ %a@;<1 -2>cannot be elaborated@]" format_obj cst_exp | ParserFailure (s, t) -> printf "@[Parser Failure:@ on %a:@ @ @[%a@]@]" - Lexer.format_token t - pp_print_text (ParserMessages.message (Parser.Aut.coq_N_of_state s)) - | ParserTimeout fuel -> - printf "@[Parser Timeout with Fuel %d@]" - fuel + Lexer.format_token t pp_print_text + (ParserMessages.message (Parser.Aut.coq_N_of_state s)) + | ParserTimeout fuel -> printf "@[Parser Timeout with Fuel %d@]" fuel diff --git a/driver/PrettyPrinter.mli b/driver/PrettyPrinter.mli new file mode 100644 index 00000000..0ceb66b9 --- /dev/null +++ b/driver/PrettyPrinter.mli @@ -0,0 +1,6 @@ +val format_obj : Format.formatter -> MclttExtracted.Syntax.Cst.obj -> unit +val format_exp : Format.formatter -> MclttExtracted.Syntax.exp -> unit +val format_nf : Format.formatter -> MclttExtracted.Syntax.nf -> unit + +val format_main_result : + Format.formatter -> MclttExtracted.Entrypoint.main_result -> unit diff --git a/driver/Test.ml b/driver/Test.ml index c25b8dcd..475cd810 100644 --- a/driver/Test.ml +++ b/driver/Test.ml @@ -1,50 +1,593 @@ (* Unit test cases for parsing *) open Main -open Entrypoint.Cst +open MclttExtracted.Entrypoint -let x, y, z = ("x", "y", "z") +(** Helper definitions *) -let%test "type" = parse "Type 5" = Some (Coq_typ 5) -let%test "nat" = parse "Nat" = Some Coq_nat -let%test "var" = parse "x" = Some (Coq_var x) -let%test "zero" = parse "zero" = Some Coq_zero -let%test "succ zero" = parse "succ zero" = Some (Coq_succ Coq_zero) +let main_of_example s = main_of_filename ("../examples/" ^ s) -let%test "lambda" = - parse "fun (x : Type 5).y" = Some (Coq_fn (x, Coq_typ 5, Coq_var y)) +(** Real tests *) +(* We never expect parser timeout. 2^500 fuel should be large enough! *) -let%test "lambda multiple args" = - parse "fun (x : Nat) (y : Nat) . x" - = Some (Coq_fn (x, Coq_nat, Coq_fn (y, Coq_nat, Coq_var x))) +let%expect_test "Type@0 is of Type@1" = + let _ = main_of_program_string "Type@0 : Type@1" in + [%expect + {| + Parsed: + Type@0 : Type@1 + Elaborated: + Type@0 : Type@1 + Normalized Result: + Type@0 : Type@1 + |}] -let%test "lambda multiple args 2" = - parse "fun (x : Nat) (y : Nat) (z : Nat) . z" - = Some - (Coq_fn (x, Coq_nat, Coq_fn (y, Coq_nat, Coq_fn (z, Coq_nat, Coq_var z)))) +let%expect_test "zero is of Nat" = + let _ = main_of_program_string "zero : Nat" in + [%expect + {| + Parsed: + 0 : Nat + Elaborated: + 0 : Nat + Normalized Result: + 0 : Nat + |}] -let%test "application" = - parse "(fun (x : Nat).x) Nat" - = Some (Coq_app (Coq_fn (x, Coq_nat, Coq_var x), Coq_nat)) +let%expect_test "zero is not of Type@0" = + let _ = main_of_program_string "zero : Type@0" in + [%expect + {| + Type Checking Failure: + 0 + is not of + Type@0 + |}] -let%test "nested 1" = - parse "(Type 5) zero" = Some (Coq_app (Coq_typ 5, Coq_zero)) +let%expect_test "succ zero is of Nat" = + let _ = main_of_program_string "succ zero : Nat" in + [%expect + {| + Parsed: + 1 : Nat + Elaborated: + 1 : Nat + Normalized Result: + 1 : Nat + |}] -let%test "nested 2" = - parse "succ (succ (succ (succ zero)))" - = Some (Coq_succ (Coq_succ (Coq_succ (Coq_succ Coq_zero)))) +let%expect_test "succ Type@0 is not of Nat (as it is ill-typed)" = + let _ = main_of_program_string "succ Type@0 : Nat" in + [%expect + {| + Type Checking Failure: + succ Type@0 + is not of + Nat + |}] -let%test "pi" = parse "pi (x:Nat).x" = Some (Coq_pi (x, Coq_nat, Coq_var x)) +let%expect_test "variable x is ill-scoped" = + let _ = main_of_program_string "x : Type@0" in + [%expect {| + Elaboration Failure: + x + cannot be elaborated + |}] -let%test "pi multiple args" = - parse "pi (x : Nat) (y : Nat) (z : Nat) . z" - = Some - (Coq_pi (x, Coq_nat, Coq_pi (y, Coq_nat, Coq_pi (z, Coq_nat, Coq_var z)))) +let%expect_test "identity function of Nat is of forall (x : Nat) -> Nat" = + let _ = main_of_program_string "fun (y : Nat) -> y : forall (x : Nat) -> Nat" in + [%expect + {| + Parsed: + fun (y : Nat) -> y : forall (x : Nat) -> Nat + Elaborated: + fun (x1 : Nat) -> x1 : forall (x1 : Nat) -> Nat + Normalized Result: + fun (x1 : Nat) -> x1 : forall (x1 : Nat) -> Nat + |}] -(* Some more finer details *) +let%expect_test "recursion on a natural number that always returns zero is of \ + Nat" = + let _ = main_of_program_string + "rec 3 return y -> Nat | zero => 0 | succ n, r => 0 end : Nat" in + [%expect + {| + Parsed: + rec 3 return y -> Nat | zero => 0 | succ n, r => 0 end : Nat + Elaborated: + rec 3 return x1 -> Nat | zero => 0 | succ x2, x3 => 0 end : Nat + Normalized Result: + 0 : Nat + |}] -let%test "pi missing colon" = parse "pi (x Nat).x" = None +let%expect_test "simple_nat.mcl works" = + let _ = main_of_example "simple_nat.mcl" in + [%expect + {| + Parsed: + 4 : Nat + Elaborated: + 4 : Nat + Normalized Result: + 4 : Nat + |}] -let%test "ignore whitespace" = - parse "fun (x \n : Type 4).x" - = Some (Coq_fn (x, Coq_typ 4, Coq_var x)) +let%expect_test "simple_rec.mcl works" = + let _ = main_of_example "simple_rec.mcl" in + [%expect + {| + Parsed: + fun (x : Nat) + -> rec x return y -> Nat | zero => 1 | succ n, r => succ r end + : forall (x : Nat) -> Nat + Elaborated: + fun (x1 : Nat) + -> rec x1 return x2 -> Nat | zero => 1 | succ x3, x4 => succ x4 end + : forall (x1 : Nat) -> Nat + Normalized Result: + fun (x1 : Nat) + -> rec x1 return x2 -> Nat | zero => 1 | succ x3, x4 => succ x4 end + : forall (x1 : Nat) -> Nat + |}] + +let%expect_test "pair.mcl works" = + let _ = main_of_example "pair.mcl" in + [%expect + {| + Parsed: + (fun (Pair : forall (A : Type@0) + (B : Type@0) + -> Type@1) + (pair : forall (A : Type@0) + (B : Type@0) + (a : A) + (b : B) + -> Pair A B) + (fst : forall (A : Type@0) + (B : Type@0) + (p : Pair A B) + -> A) + (snd : forall (A : Type@0) + (B : Type@0) + (p : Pair A B) + -> B) + -> (fun (p : Pair Nat (forall (x : Nat) -> Nat)) + -> snd Nat (forall (x : Nat) -> Nat) p + (fst Nat (forall (x : Nat) -> Nat) p)) + (pair Nat (forall (x : Nat) -> Nat) 3 + (fun (x : Nat) -> succ (succ x)))) + (fun (A : Type@0) + (B : Type@0) + -> forall (C : Type@0) + (pair : forall (a : A) + (b : B) + -> C) + -> C) + (fun (A : Type@0) + (B : Type@0) + (a : A) + (b : B) + (C : Type@0) + (pair : forall (a : A) + (b : B) + -> C) + -> pair a b) + (fun (A : Type@0) + (B : Type@0) + (p : forall (C : Type@0) + (pair : forall (a : A) + (b : B) + -> C) + -> C) + -> p A (fun (a : A) + (b : B) + -> a)) + (fun (A : Type@0) + (B : Type@0) + (p : forall (C : Type@0) + (pair : forall (a : A) + (b : B) + -> C) + -> C) + -> p B (fun (a : A) + (b : B) + -> b)) + : Nat + Elaborated: + (fun (x1 : forall (A1 : Type@0) + (A2 : Type@0) + -> Type@1) + (x2 : forall (A3 : Type@0) + (A4 : Type@0) + (x3 : A3) + (x4 : A4) + -> x1 A3 A4) + (x5 : forall (A5 : Type@0) + (A6 : Type@0) + (x6 : x1 A5 A6) + -> A5) + (x7 : forall (A7 : Type@0) + (A8 : Type@0) + (x8 : x1 A7 A8) + -> A8) + -> (fun (x9 : x1 Nat (forall (x10 : Nat) -> Nat)) + -> x7 Nat (forall (x11 : Nat) -> Nat) x9 + (x5 Nat (forall (x12 : Nat) -> Nat) x9)) + (x2 Nat (forall (x13 : Nat) -> Nat) 3 + (fun (x14 : Nat) -> succ (succ x14)))) + (fun (A9 : Type@0) + (A10 : Type@0) + -> forall (A11 : Type@0) + (x15 : forall (x16 : A9) + (x17 : A10) + -> A11) + -> A11) + (fun (A12 : Type@0) + (A13 : Type@0) + (x18 : A12) + (x19 : A13) + (A14 : Type@0) + (x20 : forall (x21 : A12) + (x22 : A13) + -> A14) + -> x20 x18 x19) + (fun (A15 : Type@0) + (A16 : Type@0) + (x23 : forall (A17 : Type@0) + (x24 : forall (x25 : A15) + (x26 : A16) + -> A17) + -> A17) + -> x23 A15 (fun (x27 : A15) + (x28 : A16) + -> x27)) + (fun (A18 : Type@0) + (A19 : Type@0) + (x29 : forall (A20 : Type@0) + (x30 : forall (x31 : A18) + (x32 : A19) + -> A20) + -> A20) + -> x29 A19 (fun (x33 : A18) + (x34 : A19) + -> x34)) + : Nat + Normalized Result: + 5 : Nat + |}] + +let%expect_test "vector.mcl works" = + let _ = main_of_example "vector.mcl" in + [%expect + {| + Parsed: + (fun (Vec : forall (A : Type@0) + (n : Nat) + -> Type@2) + (nil : forall (A : Type@0) -> Vec A 0) + (cons : forall (A : Type@0) + (n : Nat) + (head : A) + (tail : Vec A n) + -> Vec A (succ n)) + (vecRec : forall (A : Type@0) + (n : Nat) + (vec : Vec A n) + (C : forall (l : Nat) -> Type@1) + (nil : C 0) + (cons : forall (l : Nat) + (a : A) + (r : C l) + -> C (succ l)) + -> C n) + -> (fun (totalHead : forall (A : Type@0) + (n : Nat) + (vec : Vec A (succ n)) + -> A) + (vec : Vec (forall (n : Nat) -> Nat) 3) + -> totalHead (forall (n : Nat) -> Nat) 2 vec 4) + (fun (A : Type@0) + (n : Nat) + (vec : Vec A (succ n)) + -> vecRec A (succ n) vec + (fun (l : Nat) + -> rec l return r -> Type@0 + | zero => Nat + | succ l, r => A + end) + 0 + (fun (l : Nat) + (a : A) + (r : rec l return r -> Type@0 + | zero => Nat + | succ l, r => A + end) + -> a)) + (cons (forall (n : Nat) -> Nat) 2 + (fun (n : Nat) -> succ (succ (succ n))) + (cons (forall (n : Nat) -> Nat) 1 (fun (n : Nat) -> succ n) + (cons (forall (n : Nat) -> Nat) 0 + (fun (n : Nat) -> succ (succ n)) + (nil (forall (n : Nat) -> Nat)))))) + (fun (A : Type@0) + (n : Nat) + -> forall (C : forall (l : Nat) -> Type@1) + (nil : C 0) + (cons : forall (l : Nat) + (a : A) + (r : C l) + -> C (succ l)) + -> C n) + (fun (A : Type@0) + (C : forall (l : Nat) -> Type@1) + (nil : C 0) + (cons : forall (l : Nat) + (a : A) + (r : C l) + -> C (succ l)) + -> nil) + (fun (A : Type@0) + (n : Nat) + (head : A) + (tail : forall (C : forall (l : Nat) -> Type@1) + (nil : C 0) + (cons : forall (l : Nat) + (a : A) + (r : C l) + -> C (succ l)) + -> C n) + (C : forall (l : Nat) -> Type@1) + (nil : C 0) + (cons : forall (l : Nat) + (a : A) + (r : C l) + -> C (succ l)) + -> cons n head (tail C nil cons)) + (fun (A : Type@0) + (n : Nat) + (vec : forall (C : forall (l : Nat) -> Type@1) + (nil : C 0) + (cons : forall (l : Nat) + (a : A) + (r : C l) + -> C (succ l)) + -> C n) + (C : forall (l : Nat) -> Type@1) + (nil : C 0) + (cons : forall (l : Nat) + (a : A) + (r : C l) + -> C (succ l)) + -> vec C nil cons) + : Nat + Elaborated: + (fun (x1 : forall (A1 : Type@0) + (x2 : Nat) + -> Type@2) + (x3 : forall (A2 : Type@0) -> x1 A2 0) + (x4 : forall (A3 : Type@0) + (x5 : Nat) + (x6 : A3) + (x7 : x1 A3 x5) + -> x1 A3 (succ x5)) + (x8 : forall (A4 : Type@0) + (x9 : Nat) + (x10 : x1 A4 x9) + (x11 : forall (x12 : Nat) -> Type@1) + (x13 : x11 0) + (x14 : forall (x15 : Nat) + (x16 : A4) + (x17 : x11 x15) + -> x11 (succ x15)) + -> x11 x9) + -> (fun (x18 : forall (A5 : Type@0) + (x19 : Nat) + (x20 : x1 A5 (succ x19)) + -> A5) + (x21 : x1 (forall (x22 : Nat) -> Nat) 3) + -> x18 (forall (x23 : Nat) -> Nat) 2 x21 4) + (fun (A6 : Type@0) + (x24 : Nat) + (x25 : x1 A6 (succ x24)) + -> x8 A6 (succ x24) x25 + (fun (x26 : Nat) + -> rec x26 return x27 -> Type@0 + | zero => Nat + | succ x28, A7 => A6 + end) + 0 + (fun (x29 : Nat) + (x30 : A6) + (x31 : rec x29 return x32 -> Type@0 + | zero => Nat + | succ x33, A8 => A6 + end) + -> x30)) + (x4 (forall (x34 : Nat) -> Nat) 2 + (fun (x35 : Nat) -> succ (succ (succ x35))) + (x4 (forall (x36 : Nat) -> Nat) 1 (fun (x37 : Nat) -> succ x37) + (x4 (forall (x38 : Nat) -> Nat) 0 + (fun (x39 : Nat) -> succ (succ x39)) + (x3 (forall (x40 : Nat) -> Nat)))))) + (fun (A9 : Type@0) + (x41 : Nat) + -> forall (x42 : forall (x43 : Nat) -> Type@1) + (x44 : x42 0) + (x45 : forall (x46 : Nat) + (x47 : A9) + (x48 : x42 x46) + -> x42 (succ x46)) + -> x42 x41) + (fun (A10 : Type@0) + (x49 : forall (x50 : Nat) -> Type@1) + (x51 : x49 0) + (x52 : forall (x53 : Nat) + (x54 : A10) + (x55 : x49 x53) + -> x49 (succ x53)) + -> x51) + (fun (A11 : Type@0) + (x56 : Nat) + (x57 : A11) + (x58 : forall (x59 : forall (x60 : Nat) -> Type@1) + (x61 : x59 0) + (x62 : forall (x63 : Nat) + (x64 : A11) + (x65 : x59 x63) + -> x59 (succ x63)) + -> x59 x56) + (x66 : forall (x67 : Nat) -> Type@1) + (x68 : x66 0) + (x69 : forall (x70 : Nat) + (x71 : A11) + (x72 : x66 x70) + -> x66 (succ x70)) + -> x69 x56 x57 (x58 x66 x68 x69)) + (fun (A12 : Type@0) + (x73 : Nat) + (x74 : forall (x75 : forall (x76 : Nat) -> Type@1) + (x77 : x75 0) + (x78 : forall (x79 : Nat) + (x80 : A12) + (x81 : x75 x79) + -> x75 (succ x79)) + -> x75 x73) + (x82 : forall (x83 : Nat) -> Type@1) + (x84 : x82 0) + (x85 : forall (x86 : Nat) + (x87 : A12) + (x88 : x82 x86) + -> x82 (succ x86)) + -> x74 x82 x84 x85) + : Nat + Normalized Result: + 7 : Nat + |}] + +let%expect_test "nary.mcl works" = + let _ = main_of_example "nary.mcl" in + [%expect + {| + Parsed: + (fun (Nary : forall (n : Nat) -> Type@0) + (toNat : forall (f : Nary 0) -> Nat) + (appNary : forall (n : Nat) + (f : Nary (succ n)) + (arg : Nat) + -> Nary n) + (n : Nat) + (f : Nary n) + -> (rec n return y -> forall (g : Nary y) -> Nat + | zero => toNat + | succ m, r => fun (g : Nary (succ m)) -> r (appNary m g (succ m)) + end) + f) + (fun (n : Nat) + -> rec n return y -> Type@0 + | zero => Nat + | succ m, r => forall (a : Nat) -> r + end) + (fun (f : Nat) -> f) + (fun (n : Nat) + (f : rec succ n return y -> Type@0 + | zero => Nat + | succ m, r => forall (a : Nat) -> r + end) + (arg : Nat) + -> f arg) + 3 + ((fun (add : forall (a : Nat) + (b : Nat) + -> Nat) + (a : Nat) + (b : Nat) + (c : Nat) + -> add a (add b c)) + (fun (a : Nat) + (b : Nat) + -> rec a return y -> Nat | zero => b | succ m, r => succ r end)) + : Nat + Elaborated: + (fun (x1 : forall (x2 : Nat) -> Type@0) + (x3 : forall (x4 : x1 0) -> Nat) + (x5 : forall (x6 : Nat) + (x7 : x1 (succ x6)) + (x8 : Nat) + -> x1 x6) + (x9 : Nat) + (x10 : x1 x9) + -> (rec x9 return x11 -> forall (x14 : x1 x11) -> Nat + | zero => x3 + | succ x12, x13 => + fun (x15 : x1 (succ x12)) -> x13 (x5 x12 x15 (succ x12)) + end) + x10) + (fun (x16 : Nat) + -> rec x16 return x17 -> Type@0 + | zero => Nat + | succ x18, A1 => forall (x19 : Nat) -> A1 + end) + (fun (x20 : Nat) -> x20) + (fun (x21 : Nat) + (x22 : rec succ x21 return x23 -> Type@0 + | zero => Nat + | succ x24, A2 => forall (x25 : Nat) -> A2 + end) + (x26 : Nat) + -> x22 x26) + 3 + ((fun (x27 : forall (x28 : Nat) + (x29 : Nat) + -> Nat) + (x30 : Nat) + (x31 : Nat) + (x32 : Nat) + -> x27 x30 (x27 x31 x32)) + (fun (x33 : Nat) + (x34 : Nat) + -> rec x33 return x35 -> Nat + | zero => x34 + | succ x36, x37 => succ x37 + end)) + : Nat + Normalized Result: + 6 : Nat + |}] + +(* let%test "lambda" = *) +(* parse "fun (x : Type 5).y" = Some (Coq_fn (x, Coq_typ 5, Coq_var y)) *) + +(* let%test "lambda multiple args" = *) +(* parse "fun (x : Nat) (y : Nat) . x" *) +(* = Some (Coq_fn (x, Coq_nat, Coq_fn (y, Coq_nat, Coq_var x))) *) + +(* let%test "lambda multiple args 2" = *) +(* parse "fun (x : Nat) (y : Nat) (z : Nat) . z" *) +(* = Some *) +(* (Coq_fn (x, Coq_nat, Coq_fn (y, Coq_nat, Coq_fn (z, Coq_nat, Coq_var z)))) *) + +(* let%test "application" = *) +(* parse "(fun (x : Nat).x) Nat" *) +(* = Some (Coq_app (Coq_fn (x, Coq_nat, Coq_var x), Coq_nat)) *) + +(* let%test "nested 1" = *) +(* parse "(Type 5) zero" = Some (Coq_app (Coq_typ 5, Coq_zero)) *) + +(* let%test "nested 2" = *) +(* parse "succ (succ (succ (succ zero)))" *) +(* = Some (Coq_succ (Coq_succ (Coq_succ (Coq_succ Coq_zero)))) *) + +(* let%test "pi" = parse "pi (x:Nat).x" = Some (Coq_pi (x, Coq_nat, Coq_var x)) *) + +(* let%test "pi multiple args" = *) +(* parse "pi (x : Nat) (y : Nat) (z : Nat) . z" *) +(* = Some *) +(* (Coq_pi (x, Coq_nat, Coq_pi (y, Coq_nat, Coq_pi (z, Coq_nat, Coq_var z)))) *) + +(* (\* Some more finer details *\) *) + +(* let%test "pi missing colon" = parse "pi (x Nat).x" = None *) + +(* let%test "ignore whitespace" = *) +(* parse "fun (x \n : Type 4).x" *) +(* = Some (Coq_fn (x, Coq_typ 4, Coq_var x)) *) diff --git a/driver/dune b/driver/dune index 1a350682..40e0c90a 100644 --- a/driver/dune +++ b/driver/dune @@ -2,16 +2,18 @@ (library (name Mcltt) - (public_name mcltt.Driver) - (modules Main Lexer PrettyPrinter) + (public_name mcltt.driver) + (modules Main Lexer PrettyPrinter Test) (libraries MclttExtracted) - ; (inline_tests) - ; (preprocess - ; (pps ppx_inline_test)) -) + (inline_tests + (deps + (glob_files_rec ../examples/*.mcl))) + (preprocess + (pps ppx_expect))) (executable (name mcltt) + (public_name mcltt) (modules Mcltt) (libraries Mcltt)) diff --git a/driver/extracted/.ocamlformat b/driver/extracted/.ocamlformat new file mode 100644 index 00000000..fc8e62b7 --- /dev/null +++ b/driver/extracted/.ocamlformat @@ -0,0 +1 @@ +disable = true \ No newline at end of file diff --git a/driver/extracted/dune b/driver/extracted/dune index 233b358d..aa468c51 100644 --- a/driver/extracted/dune +++ b/driver/extracted/dune @@ -1,5 +1,5 @@ (library (name MclttExtracted) - (public_name mcltt.Extracted)) + (public_name mcltt.extracted)) (documentation) diff --git a/dune-project b/dune-project index 88b2a868..e141f18b 100644 --- a/dune-project +++ b/dune-project @@ -1,3 +1,34 @@ (lang dune 3.13) (using menhir 2.1) (name mcltt) + +(generate_opam_files) + +(license MIT) +(authors "Antoine Gaulin" + "Jason Hu" + "Junyoung Jang") +(maintainers "the CompLogic group, McGill University") +(homepage "https://beluga-lang.github.io/McLTT/") +(documentation "https://beluga-lang.github.io/McLTT/") +(source (github Beluga-lang/McLTT)) + +(package + (name mcltt) + (synopsis "A Bottom-up Approach to Implementing A Proof Assistant") + (description "In McLTT, we build a verified, runnable typechecker for Martin-Löf type theory. After +the accomplishment of this project, we will obtain an executable, to which we can feed +a program in Martin-Loef type theory, and this executable will check whether this +program has the specified type. McLTT is novel in that it is implemented in +Coq. Moreover, we will prove that the typechecking algorithm extracted from Coq is +sound and complete: a program passes typechecking if and only if it is a well-typed +program in MLTT. This will be the first verified proof assistant (despite being +elementary) and serves as a basis for future extensions.") + (depends + (ocaml (>= "4.14.2")) + (coq (= "8.20.0")) + (menhir (= "20240715")) + (coq-menhirlib (= "20240715")) + (coq-equations (= "1.3.1+8.20")) + (ppx_expect (>= "0.16.0")) + (sherlodoc :with-doc))) diff --git a/mcltt.opam.template b/mcltt.opam.template new file mode 100644 index 00000000..3d7af790 --- /dev/null +++ b/mcltt.opam.template @@ -0,0 +1,5 @@ +build: [ + [make] + [make coqdoc] {with-doc} +] + diff --git a/theories/CoqMakefile.mk.local-late b/theories/CoqMakefile.mk.local-late index 336904ec..d0e3ee8a 100644 --- a/theories/CoqMakefile.mk.local-late +++ b/theories/CoqMakefile.mk.local-late @@ -6,11 +6,13 @@ MENHIR := menhir PARSERMESSAGEBASE := parserMessages.messages FRONTENDDIR := Frontend PARSERMESSAGEFILE := $(FRONTENDDIR)/$(PARSERMESSAGEBASE) +PARSERMESSAGETEMPFILE := $(PARSERMESSAGEFILE:%.messages=%.messages.temp) +PARSERMESSAGEMERGEFILE := $(PARSERMESSAGEFILE:%.messages=%.messages.merge) PARSERMESSAGEEXTRACTIONFILE := ../driver/extracted/ParserMessages.ml COQPARSERORIGFILE := $(shell find ./ -name '*.vy') COQPARSERFILE := $(COQPARSERORIGFILE:%.vy=%.v) -post-all:: $(COQPARSERFILE) $(PARSERMESSAGEEXTRACTIONFILE) +post-all:: $(COQPARSERFILE) $(PARSERMESSAGEEXTRACTIONFILE) check_parserMessages @echo "Separate Extraction main." | $(COQTOP) $(COQFLAGS) $(COQLIBS) -topfile Entrypoint.v -l Entrypoint.v $(COQPARSERFILE): %.v : %.vy @@ -18,3 +20,16 @@ $(COQPARSERFILE): %.v : %.vy $(PARSERMESSAGEEXTRACTIONFILE): $(PARSERMESSAGEFILE) $(COQPARSERORIGFILE) $(MENHIR) --coq "$(COQPARSERORIGFILE)" --compile-errors "$(PARSERMESSAGEFILE)" > "$(PARSERMESSAGEEXTRACTIONFILE)" + +.PHONY: check_parserMessages +check_parserMessages: + $(MENHIR) --coq "$(COQPARSERORIGFILE)" --list-errors > "$(PARSERMESSAGETEMPFILE)" + $(MENHIR) --coq "$(COQPARSERORIGFILE)" --compare-errors "$(PARSERMESSAGETEMPFILE)" --compare-errors "$(PARSERMESSAGEFILE)" + rm "$(PARSERMESSAGETEMPFILE)" + +.PHONY: update_parserMessages +update_parserMessages: + $(MENHIR) --coq "$(COQPARSERORIGFILE)" --list-errors > "$(PARSERMESSAGETEMPFILE)" + $(MENHIR) --coq "$(COQPARSERORIGFILE)" --merge-errors "$(PARSERMESSAGETEMPFILE)" --merge-errors "$(PARSERMESSAGEFILE)" > "$(PARSERMESSAGEMERGEFILE)" + rm "$(PARSERMESSAGETEMPFILE)" + mv "$(PARSERMESSAGEMERGEFILE)" "$(PARSERMESSAGEFILE)" diff --git a/theories/Core/Semantic/PER/CoreTactics.v b/theories/Core/Semantic/PER/CoreTactics.v index 597b40c6..b61de31d 100644 --- a/theories/Core/Semantic/PER/CoreTactics.v +++ b/theories/Core/Semantic/PER/CoreTactics.v @@ -46,8 +46,7 @@ Ltac destruct_rel_typ := Ltac basic_invert_per_univ_elem H := progress simp per_univ_elem in H; - inversion H; - subst; + dependent destruction H; try rewrite <- per_univ_elem_equation_1 in *. Ltac basic_per_univ_elem_econstructor := diff --git a/theories/Core/Semantic/PER/Lemmas.v b/theories/Core/Semantic/PER/Lemmas.v index 826c36ea..f742ff19 100644 --- a/theories/Core/Semantic/PER/Lemmas.v +++ b/theories/Core/Semantic/PER/Lemmas.v @@ -446,10 +446,10 @@ Lemma per_univ_elem_trans : forall i R a1 a2, R m1 m2 -> R m2 m3 -> R m1 m3). -Proof with (basic_per_univ_elem_econstructor; mautosolve). +Proof with (basic_per_univ_elem_econstructor; mautosolve 4). induction 1 using per_univ_elem_ind; [> split; - [ intros * HT2; basic_invert_per_univ_elem HT2; clear HT2 + [ intros * HT2; basic_invert_per_univ_elem HT2 | intros * HTR1 HTR2; apply_relation_equivalence ] ..]; mauto. - (* univ case *) subst. diff --git a/theories/Core/Soundness/FunctionCases.v b/theories/Core/Soundness/FunctionCases.v index 8134cf2c..c0b463ce 100644 --- a/theories/Core/Soundness/FunctionCases.v +++ b/theories/Core/Soundness/FunctionCases.v @@ -364,7 +364,7 @@ Proof. assert {{ Γ ⊩ A : Type@(max i j) }} by mauto 3. assert {{ ⊩ Γ, A }} by mauto 2. assert (i <= max i j) by lia. - assert {{ Γ, A ⊢ Type@i ⊆ Type@(max i j) }} by mauto 3. + assert {{ Γ, A ⊢ Type@i ⊆ Type@(max i j) }} by mauto 4. assert {{ Γ, A ⊩ B : Type@(max i j) }} by mauto 3. mauto 2 using glu_rel_exp_app_helper. Qed. diff --git a/theories/Core/Soundness/LogicalRelation/CoreLemmas.v b/theories/Core/Soundness/LogicalRelation/CoreLemmas.v index 976d2247..515bdafa 100644 --- a/theories/Core/Soundness/LogicalRelation/CoreLemmas.v +++ b/theories/Core/Soundness/LogicalRelation/CoreLemmas.v @@ -59,7 +59,7 @@ Lemma glu_nat_resp_exp_eq : forall Γ M a, {{ Γ ⊢ M ≈ M' : ℕ }} -> glu_nat Γ M' a. Proof. - induction 1; intros; mauto. + induction 1; intros; mauto 4. econstructor; trivial. intros. transitivity {{{ M[σ] }}}; mauto. @@ -82,11 +82,11 @@ Lemma glu_nat_readback : forall Γ M a, {{ Δ ⊢ M[σ] ≈ M' : ℕ }}. Proof. induction 1; intros; progressive_inversion; gen_presups. - - transitivity {{{ zero[σ] }}}; mauto. - - assert {{ Δ ⊢ M'[σ] ≈ M0 : ℕ }} by mauto. + - transitivity {{{ zero[σ] }}}; mauto 4. + - assert {{ Δ ⊢ M'[σ] ≈ M0 : ℕ }} by mauto 4. transitivity {{{ (succ M')[σ] }}}; mauto 3. transitivity {{{ succ M'[σ] }}}; mauto 4. - - mauto. + - mauto 4. Qed. #[global] @@ -270,7 +270,7 @@ Lemma glu_univ_elem_per_elem : forall i P El a, Proof. simpl. induction 1 using glu_univ_elem_ind; intros; - try do 2 match_by_head1 per_univ_elem invert_per_univ_elem; + match_by_head per_univ_elem ltac:(fun H => directed invert_per_univ_elem H); simpl_glu_rel; try fold (per_univ j m m); mauto 4. diff --git a/theories/Core/Soundness/LogicalRelation/CoreTactics.v b/theories/Core/Soundness/LogicalRelation/CoreTactics.v index 2b85a547..34bd7691 100644 --- a/theories/Core/Soundness/LogicalRelation/CoreTactics.v +++ b/theories/Core/Soundness/LogicalRelation/CoreTactics.v @@ -5,8 +5,7 @@ From Mcltt.Core.Soundness Require Import LogicalRelation.Definitions. Ltac basic_invert_glu_univ_elem H := progress simp glu_univ_elem in H; - inversion H; - subst; + dependent destruction H; try rewrite <- glu_univ_elem_equation_1 in *. Ltac basic_glu_univ_elem_econstructor := diff --git a/theories/Core/Soundness/LogicalRelation/Lemmas.v b/theories/Core/Soundness/LogicalRelation/Lemmas.v index df1780b4..dd432266 100644 --- a/theories/Core/Soundness/LogicalRelation/Lemmas.v +++ b/theories/Core/Soundness/LogicalRelation/Lemmas.v @@ -405,6 +405,7 @@ Proof. assert {{ Γ ⊢ A'[Id] ≈ V : Type@i }} as -> by mauto 4. eapply wf_subtyp_refl'. mauto 4. + - bulky_rewrite. - bulky_rewrite. mauto 3. - destruct_by_head pi_glu_typ_pred. @@ -916,7 +917,7 @@ Qed. Ltac invert_glu_ctx_env H := (unshelve eapply (glu_ctx_env_cons_clean_inversion _) in H; shelve_unifiable; [eassumption |]; destruct H as [? [? []]]) - + (inversion H; subst). + + dependent destruction H. Lemma glu_ctx_env_subtyp_sub_if : forall Γ Γ' Sb Sb' Δ σ ρ, {{ ⊢ Γ ⊆ Γ' }} -> diff --git a/theories/Core/Soundness/SubstitutionCases.v b/theories/Core/Soundness/SubstitutionCases.v index 5fd963f7..8858e558 100644 --- a/theories/Core/Soundness/SubstitutionCases.v +++ b/theories/Core/Soundness/SubstitutionCases.v @@ -59,7 +59,7 @@ Proof. intros * [SbΓA]. match_by_head1 glu_ctx_env invert_glu_ctx_env. handle_functional_glu_ctx_env. - do 2 eexists; repeat split; mauto. + do 2 eexists; repeat split; [econstructor | |]; try reflexivity; mauto. intros. destruct_by_head cons_glu_sub_pred. econstructor; mauto. diff --git a/theories/Extraction/Evaluation.v b/theories/Extraction/Evaluation.v index c267ce32..aecd587b 100644 --- a/theories/Extraction/Evaluation.v +++ b/theories/Extraction/Evaluation.v @@ -72,7 +72,7 @@ with eval_sub_order : sub -> env -> Prop := eval_sub_order {{{ σ ∘ τ }}} p ). #[local] - Hint Constructors eval_exp_order eval_natrec_order eval_app_order eval_sub_order : mcltt. +Hint Constructors eval_exp_order eval_natrec_order eval_app_order eval_sub_order : mcltt. Lemma eval_exp_order_sound : forall m p a, @@ -95,11 +95,11 @@ Proof with (econstructor; intros; functional_eval_rewrite_clear; eauto). Qed. #[export] - Hint Resolve eval_exp_order_sound eval_natrec_order_sound eval_app_order_sound eval_sub_order_sound : mcltt. +Hint Resolve eval_exp_order_sound eval_natrec_order_sound eval_app_order_sound eval_sub_order_sound : mcltt. #[local] - Ltac impl_obl_tac1 := +Ltac impl_obl_tac1 := match goal with | H : eval_exp_order _ _ |- _ => progressive_invert H | H : eval_natrec_order _ _ _ _ _ |- _ => progressive_invert H @@ -108,13 +108,13 @@ Qed. end. #[local] - Ltac impl_obl_tac := +Ltac impl_obl_tac := repeat impl_obl_tac1; try econstructor; eauto. Derive Signature for eval_exp_order eval_natrec_order eval_app_order eval_sub_order. -#[tactic="impl_obl_tac"] - Equations eval_exp_impl m p (H : eval_exp_order m p) : { d | eval_exp m p d } by struct H := +#[tactic="impl_obl_tac",derive(equations=no,eliminator=no)] +Equations eval_exp_impl m p (H : eval_exp_order m p) : { d | eval_exp m p d } by struct H := | {{{ Type@i }}}, p, H => exist _ d{{{ 𝕌@i }}} _ | {{{ #x }}} , p, H => exist _ (p x) _ | {{{ ℕ }}} , p, H => exist _ d{{{ ℕ }}} _ @@ -140,7 +140,7 @@ Derive Signature for eval_exp_order eval_natrec_order eval_app_order eval_sub_or let (m, Hm) := eval_exp_impl M p' _ in exist _ m _ - with eval_natrec_impl A MZ MS m p (H : eval_natrec_order A MZ MS m p) : { d | eval_natrec A MZ MS m p d } by struct H := +with eval_natrec_impl A MZ MS m p (H : eval_natrec_order A MZ MS m p) : { d | eval_natrec A MZ MS m p d } by struct H := | A, MZ, MS, d{{{ zero }}} , p, H => let (mz, Hmz) := eval_exp_impl MZ p _ in exist _ mz _ @@ -153,7 +153,7 @@ Derive Signature for eval_exp_order eval_natrec_order eval_app_order eval_sub_or let (mA, HmA) := eval_exp_impl A d{{{ p ↦ ⇑ ℕ m }}} _ in exist _ d{{{ ⇑ mA (rec m under p return A | zero -> mz | succ -> MS end) }}} _ - with eval_app_impl m n (H : eval_app_order m n) : { d | eval_app m n d } by struct H := +with eval_app_impl m n (H : eval_app_order m n) : { d | eval_app m n d } by struct H := | d{{{ λ p M }}} , n, H => let (m, Hm) := eval_exp_impl M d{{{ p ↦ n }}} _ in exist _ m _ @@ -161,7 +161,7 @@ Derive Signature for eval_exp_order eval_natrec_order eval_app_order eval_sub_or let (b, Hb) := eval_exp_impl B d{{{ p ↦ n }}} _ in exist _ d{{{ ⇑ b (m (⇓ a n)) }}} _ - with eval_sub_impl s p (H : eval_sub_order s p) : { p' | eval_sub s p p' } by struct H := +with eval_sub_impl s p (H : eval_sub_order s p) : { p' | eval_sub s p p' } by struct H := | {{{ Id }}}, p, H => exist _ p _ | {{{ Wk }}}, p, H => exist _ d{{{ p↯ }}} _ | {{{ s ,, M }}}, p, H => @@ -173,102 +173,55 @@ Derive Signature for eval_exp_order eval_natrec_order eval_app_order eval_sub_or let (p'', Hp'') := eval_sub_impl s p' _ in exist _ p'' _. -(* I don't understand why these obligations must be defined separately *) -(* c.f. https://github.com/mattam82/Coq-Equations/issues/598 *) -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. - Extraction Inline eval_exp_impl_functional eval_natrec_impl_functional eval_app_impl_functional eval_sub_impl_functional. - -Ltac edes_rewrite Hind := - let Heq := fresh "Heq" in - edestruct Hind as [? Heq]; - try rewrite Heq in *. - - -Ltac complete_tac := - match goal with - | Hind : context[exists _, ?f _ _ = _] |- exists _, (let (_, _) := ?f _ _ in _) = _ => - edes_rewrite Hind - | Hind : context[exists _, ?f _ _ _ = _] |- exists _, (let (_, _) := ?f _ _ _ in _) = _ => - edes_rewrite Hind - | Hind : context[exists _, ?f _ _ _ _ = _] |- exists _, (let (_, _) := ?f _ _ _ _ in _) = _ => - edes_rewrite Hind - | Hind : context[exists _, ?f _ _ _ _ _ = _] |- exists _, (let (_, _) := ?f _ _ _ _ _ in _) = _ => - edes_rewrite Hind - | Hind : context[exists _, ?f _ _ _ _ _ _ = _] |- exists _, (let (_, _) := ?f _ _ _ _ _ _ in _) = _ => - edes_rewrite Hind - end. - -(* The definitions of *_impl already come with soundness proofs, so we only need to prove completeness *) - -Lemma eval_exp_impl_complete' : forall M p m, - {{ ⟦ M ⟧ p ↘ m }} -> - forall (H : eval_exp_order M p), - exists H', eval_exp_impl M p H = exist _ m H' -with eval_natrec_impl_complete' : forall A MZ MS m p r, - {{ rec m ⟦return A | zero -> MZ | succ -> MS end⟧ p ↘ r }} -> - forall (H : eval_natrec_order A MZ MS m p), - exists H', eval_natrec_impl A MZ MS m p H = exist _ r H' -with eval_app_impl_complete' : forall m n r, - {{ $| m & n |↘ r }} -> - forall (H : eval_app_order m n), - exists H', eval_app_impl m n H = exist _ r H' -with eval_sub_impl_complete' : forall σ p p', - {{ ⟦ σ ⟧s p ↘ p' }} -> - forall (H : eval_sub_order σ p), - exists H', eval_sub_impl σ p H = exist _ p' H'. -Proof with (intros; - simp eval_exp_impl; - repeat complete_tac; - eauto). - - clear eval_exp_impl_complete'; induction 1... - - clear eval_natrec_impl_complete'; induction 1... - - clear eval_app_impl_complete'; induction 1... - - clear eval_sub_impl_complete'; induction 1... -Qed. +(** The definitions of eval__*_impl already come with soundness proofs, + so we only need to prove completeness. However, the completeness + is also obvious from the soundness of eval orders and functional + nature of eval. *) #[local] - Hint Resolve eval_exp_impl_complete' - eval_natrec_impl_complete' - eval_app_impl_complete' - eval_sub_impl_complete' : mcltt. +Ltac functional_eval_complete := + lazymatch goal with + | |- exists (_ : ?T), _ => + let Horder := fresh "Horder" in + assert T as Horder by mauto 3; + eexists Horder; + lazymatch goal with + | |- exists _, ?L = _ => + destruct L; + functional_eval_rewrite_clear; + eexists; reflexivity + end + end. Lemma eval_exp_impl_complete : forall M p m, {{ ⟦ M ⟧ p ↘ m }} -> exists H H', eval_exp_impl M p H = exist _ m H'. Proof. - repeat unshelve mauto. + intros; functional_eval_complete. Qed. Lemma eval_natrec_impl_complete : forall A MZ MS m p r, {{ rec m ⟦return A | zero -> MZ | succ -> MS end⟧ p ↘ r }} -> exists H H', eval_natrec_impl A MZ MS m p H = exist _ r H'. Proof. - repeat unshelve mauto. + intros; functional_eval_complete. Qed. Lemma eval_app_impl_complete : forall m n r, {{ $| m & n |↘ r }} -> exists H H', eval_app_impl m n H = exist _ r H'. Proof. - repeat unshelve mauto. + intros; functional_eval_complete. Qed. Lemma eval_sub_impl_complete : forall σ p p', {{ ⟦ σ ⟧s p ↘ p' }} -> exists H H', eval_sub_impl σ p H = exist _ p' H'. Proof. - repeat unshelve mauto. + intros; functional_eval_complete. Qed. diff --git a/theories/Extraction/NbE.v b/theories/Extraction/NbE.v index fa5ff9f3..3d75800b 100644 --- a/theories/Extraction/NbE.v +++ b/theories/Extraction/NbE.v @@ -15,7 +15,7 @@ Inductive initial_env_order : ctx -> Prop := initial_env_order (A :: Γ)). #[local] - Hint Constructors initial_env_order : mcltt. +Hint Constructors initial_env_order : mcltt. Lemma initial_env_order_sound : forall Γ p, initial_env Γ p -> @@ -25,24 +25,24 @@ Proof with (econstructor; intros; functional_initial_env_rewrite_clear; function Qed. #[local] - Hint Resolve initial_env_order_sound : mcltt. +Hint Resolve initial_env_order_sound : mcltt. Derive Signature for initial_env_order. Section InitialEnvImpl. #[local] - Ltac impl_obl_tac1 := + Ltac impl_obl_tac1 := match goal with | H : initial_env_order _ |- _ => progressive_invert H end. #[local] - Ltac impl_obl_tac := + Ltac impl_obl_tac := repeat impl_obl_tac1; try econstructor; mauto. - #[tactic="impl_obl_tac"] - Equations initial_env_impl G (H : initial_env_order G) : { p | initial_env G p } by struct H := + #[tactic="impl_obl_tac",derive(equations=no,eliminator=no)] + Equations initial_env_impl G (H : initial_env_order G) : { p | initial_env G p } by struct H := | nil, H => exist _ empty_env _ | cons A G, H => let (p, Hp) := initial_env_impl G _ in @@ -51,30 +51,41 @@ Section InitialEnvImpl. End InitialEnvImpl. - -Lemma initial_env_impl_complete' : forall G p, - initial_env G p -> - forall (H : initial_env_order G), - exists H', initial_env_impl G H = exist _ p H'. -Proof. - induction 1; - pose proof eval_exp_impl_complete'; - intros; simp initial_env_impl; - do 2 try complete_tac; - eauto. -Qed. - -#[local] -Hint Resolve initial_env_impl_complete' : mcltt. - +(** The definitions of [initial_env_impl] already come with soundness proofs, + so we only need to prove completeness. However, the completeness + is also obvious from the soundness of eval orders and functional + nature of initial_env. *) Lemma initial_env_impl_complete : forall G p, initial_env G p -> exists H H', initial_env_impl G H = exist _ p H'. Proof. - repeat unshelve mauto. + intros. + assert (Horder : initial_env_order G) by mauto. + exists Horder. + destruct (initial_env_impl G Horder). + functional_initial_env_rewrite_clear. + eexists; reflexivity. Qed. +(** A similar approach works for nbe implementations. + However, as we have 2 implementations (each for [nbe] and [nbe_ty]), + We define a tactic to deal with both cases. *) + +Ltac functional_nbe_complete := + lazymatch goal with + | |- exists (_ : ?T), _ => + let Horder := fresh "Horder" in + assert T as Horder by mauto 3; + eexists Horder; + lazymatch goal with + | |- exists _, ?L = _ => + destruct L; + functional_nbe_rewrite_clear; + eexists; reflexivity + end + end. + Inductive nbe_order G M A : Prop := | nbe_order_run : `( initial_env_order G -> @@ -90,8 +101,7 @@ Inductive nbe_order G M A : Prop := nbe_order G M A ). #[local] - Hint Constructors nbe_order : mcltt. - +Hint Constructors nbe_order : mcltt. Lemma nbe_order_sound : forall G M A w, nbe G M A w -> @@ -105,22 +115,22 @@ Proof with (econstructor; intros; Qed. #[local] - Hint Resolve nbe_order_sound : mcltt. +Hint Resolve nbe_order_sound : mcltt. Section NbEDef. #[local] - Ltac impl_obl_tac1 := + Ltac impl_obl_tac1 := match goal with | H : nbe_order _ _ _ |- _ => progressive_invert H end. #[local] - Ltac impl_obl_tac := + Ltac impl_obl_tac := repeat impl_obl_tac1; try econstructor; mauto. - #[tactic="impl_obl_tac"] - Equations nbe_impl G M A (H : nbe_order G M A) : { w | nbe G M A w } by struct H := + #[tactic="impl_obl_tac",derive(equations=no,eliminator=no)] + Equations nbe_impl G M A (H : nbe_order G M A) : { w | nbe G M A w } by struct H := | G, M, A, H => let (p, Hp) := initial_env_impl G _ in let (a, Ha) := eval_exp_impl A p _ in @@ -128,33 +138,15 @@ Section NbEDef. let (w, Hw) := read_nf_impl (length G) d{{{ ⇓ a m }}} _ in exist _ w _. - Lemma nbe_impl_complete' : forall G M A w, - nbe G M A w -> - forall (H : nbe_order G M A), - exists H', nbe_impl G M A H = exist _ w H'. - Proof. - induction 1; - pose proof initial_env_impl_complete'; - pose proof eval_exp_impl_complete'; - pose proof read_nf_impl_complete'; - intros; simp nbe_impl; - repeat complete_tac; - eauto. - Qed. - End NbEDef. -#[local] - Hint Resolve nbe_impl_complete' : mcltt. - Lemma nbe_impl_complete : forall G M A w, nbe G M A w -> - exists H H', nbe_impl G M A H = exist _ w H'. + exists H H', nbe_impl G M A H = exist _ w H'. Proof. - repeat unshelve mauto. + intros; functional_nbe_complete. Qed. - Inductive nbe_ty_order G A : Prop := | nbe_ty_order_run : `( initial_env_order G -> @@ -167,8 +159,7 @@ Inductive nbe_ty_order G A : Prop := nbe_ty_order G A ). #[local] - Hint Constructors nbe_ty_order : mcltt. - +Hint Constructors nbe_ty_order : mcltt. Lemma nbe_ty_order_sound : forall G A w, nbe_ty G A w -> @@ -182,22 +173,22 @@ Proof with (econstructor; intros; Qed. #[local] - Hint Resolve nbe_ty_order_sound : mcltt. +Hint Resolve nbe_ty_order_sound : mcltt. Section NbETyDef. #[local] - Ltac impl_obl_tac1 := + Ltac impl_obl_tac1 := match goal with | H : nbe_ty_order _ _ |- _ => progressive_invert H end. #[local] - Ltac impl_obl_tac := + Ltac impl_obl_tac := repeat impl_obl_tac1; try econstructor; mauto. - #[tactic="impl_obl_tac"] - Equations nbe_ty_impl G A (H : nbe_ty_order G A) : { w | nbe_ty G A w } by struct H := + #[tactic="impl_obl_tac",derive(equations=no,eliminator=no)] + Equations nbe_ty_impl G A (H : nbe_ty_order G A) : { w | nbe_ty G A w } by struct H := | G, A, H => let (p, Hp) := initial_env_impl G _ in let (a, Ha) := eval_exp_impl A p _ in @@ -206,26 +197,9 @@ Section NbETyDef. End NbETyDef. -Lemma nbe_ty_impl_complete' : forall G A w, - nbe_ty G A w -> - forall (H : nbe_ty_order G A), - exists H', nbe_ty_impl G A H = exist _ w H'. -Proof. - induction 1; - pose proof initial_env_impl_complete'; - pose proof eval_exp_impl_complete'; - pose proof read_typ_impl_complete'; - intros; simp nbe_ty_impl; - repeat complete_tac; - eauto. -Qed. - -#[local] - Hint Resolve nbe_ty_impl_complete' : mcltt. - Lemma nbe_ty_impl_complete : forall G A w, nbe_ty G A w -> - exists H H', nbe_ty_impl G A H = exist _ w H'. + exists H H', nbe_ty_impl G A H = exist _ w H'. Proof. - repeat unshelve mauto. + intros; functional_nbe_complete. Qed. diff --git a/theories/Extraction/PseudoMonadic.v b/theories/Extraction/PseudoMonadic.v index ea1900c1..7abb5266 100644 --- a/theories/Extraction/PseudoMonadic.v +++ b/theories/Extraction/PseudoMonadic.v @@ -1,8 +1,9 @@ From Coq Require Extraction. -(* We cannot use class based generalization for - the following definitions as Coq does not support - polymorphism across [Prop] and [Set] *) +(** We cannot use class based generalization for + the following definitions as Coq does not support + extractable polymorphism across [Prop] and [Set]. + #See this Coq issue# *) Definition sumbool_failable_bind {A B} (ab : {A} + {B}) {C D : Prop} (fail : B -> D) (next : A -> {C} + {D}) := match ab with diff --git a/theories/Extraction/Readback.v b/theories/Extraction/Readback.v index 1f990b43..9eb7bcde 100644 --- a/theories/Extraction/Readback.v +++ b/theories/Extraction/Readback.v @@ -76,8 +76,7 @@ with read_typ_order : nat -> domain -> Prop := read_typ_order s d{{{ ⇑ a b }}} ). #[local] - Hint Constructors read_nf_order read_ne_order read_typ_order : mcltt. - +Hint Constructors read_nf_order read_ne_order read_typ_order : mcltt. Lemma read_nf_order_sound : forall s d m, {{ Rnf d in s ↘ m }} -> @@ -95,13 +94,12 @@ Proof with (econstructor; intros; functional_eval_rewrite_clear; mauto). Qed. #[export] - Hint Resolve read_nf_order_sound read_ne_order_sound read_typ_order_sound : mcltt. +Hint Resolve read_nf_order_sound read_ne_order_sound read_typ_order_sound : mcltt. Derive Signature for read_nf_order read_ne_order read_typ_order. - #[local] - Ltac impl_obl_tac1 := +Ltac impl_obl_tac1 := match goal with | H : read_nf_order _ _ |- _ => progressive_invert H | H : read_ne_order _ _ |- _ => progressive_invert H @@ -109,10 +107,10 @@ Derive Signature for read_nf_order read_ne_order read_typ_order. end. #[local] - Ltac impl_obl_tac := +Ltac impl_obl_tac := repeat impl_obl_tac1; try econstructor; mauto. -#[tactic="impl_obl_tac"] +#[tactic="impl_obl_tac",derive(equations=no,eliminator=no)] Equations read_nf_impl s d (H : read_nf_order s d) : { m | {{ Rnf d in s ↘ m }} } by struct H := | s, d{{{ ⇓ 𝕌@i a }}} , H => let (A, HA) := read_typ_impl s a _ in @@ -163,69 +161,47 @@ Equations read_nf_impl s d (H : read_nf_order s d) : { m | {{ Rnf d in s ↘ m } let (B, HB) := read_ne_impl s b _ in exist _ n{{{ ⇑ B }}} _. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. -Next Obligation. impl_obl_tac. Defined. - Extraction Inline read_nf_impl_functional read_ne_impl_functional read_typ_impl_functional. -#[local] - Ltac pose_other_lemmas := - pose proof eval_exp_impl_complete'; - pose proof eval_natrec_impl_complete'; - pose proof eval_app_impl_complete'; - pose proof eval_sub_impl_complete'. - -Lemma read_nf_impl_complete' : forall s d m, - {{ Rnf d in s ↘ m }} -> - forall (H : read_nf_order s d), - exists H', read_nf_impl s d H = exist _ m H' -with read_ne_impl_complete' : forall s d m, - {{ Rne d in s ↘ m }} -> - forall (H : read_ne_order s d), - exists H', read_ne_impl s d H = exist _ m H' -with read_typ_impl_complete' : forall s d m, - {{ Rtyp d in s ↘ m }} -> - forall (H : read_typ_order s d), - exists H', read_typ_impl s d H = exist _ m H'. -Proof with (pose_other_lemmas; (* so that complete_tac uses these lemmas *) - intros; - simp read_nf_impl; - repeat complete_tac; - eauto). - - clear read_nf_impl_complete'; induction 1... - - clear read_ne_impl_complete'; induction 1... - - clear read_typ_impl_complete'; induction 1... -Qed. +(** The definitions of read__*_impl already come with soundness proofs, + so we only need to prove completeness. However, the completeness + is also obvious from the soundness of eval orders and functional + nature of readback. *) #[local] - Hint Resolve read_nf_impl_complete' - read_ne_impl_complete' - read_typ_impl_complete' : mcltt. +Ltac functional_read_complete := + lazymatch goal with + | |- exists (_ : ?T), _ => + let Horder := fresh "Horder" in + assert T as Horder by mauto 3; + eexists Horder; + lazymatch goal with + | |- exists _, ?L = _ => + destruct L; + functional_read_rewrite_clear; + eexists; reflexivity + end + end. Lemma read_nf_impl_complete : forall s d m, {{ Rnf d in s ↘ m }} -> exists H H', read_nf_impl s d H = exist _ m H'. Proof. - repeat unshelve mauto. + intros; functional_read_complete. Qed. Lemma read_ne_impl_complete : forall s d m, {{ Rne d in s ↘ m }} -> exists H H', read_ne_impl s d H = exist _ m H'. Proof. - repeat unshelve mauto. + intros; functional_read_complete. Qed. Lemma read_typ_impl_complete : forall s d m, {{ Rtyp d in s ↘ m }} -> exists H H', read_typ_impl s d H = exist _ m H'. Proof. - repeat unshelve mauto. + intros; functional_read_complete. Qed. diff --git a/theories/Extraction/Subtyping.v b/theories/Extraction/Subtyping.v index b7c585ad..48fc5de7 100644 --- a/theories/Extraction/Subtyping.v +++ b/theories/Extraction/Subtyping.v @@ -21,7 +21,7 @@ Ltac subtyping_tac := try congruence end. -#[tactic="subtyping_tac"] +#[tactic="subtyping_tac",derive(equations=no,eliminator=no)] Equations subtyping_nf_impl A B : { {{ ⊢anf A ⊆ B }} } + {~ {{ ⊢anf A ⊆ B }} } := | n{{{ Type@i }}}, n{{{ Type@j }}} => let*b _ := Compare_dec.le_lt_dec i j while _ in @@ -36,6 +36,10 @@ Equations subtyping_nf_impl A B : { {{ ⊢anf A ⊆ B }} } + {~ {{ ⊢anf A ⊆ | left _ => left _ | right _ => right _ }. + +(** The definitions of [subtyping_nf_impl] already come with soundness proofs, + as well as obvious completeness. *) + Theorem subtyping_nf_impl_complete : forall A B, {{ ⊢anf A ⊆ B }} -> exists H, subtyping_nf_impl A B = left H. @@ -71,7 +75,7 @@ Ltac subtyping_impl_tac1 := Ltac subtyping_impl_tac := repeat subtyping_impl_tac1; try econstructor; mauto. -#[tactic="subtyping_impl_tac"] +#[tactic="subtyping_impl_tac",derive(equations=no,eliminator=no)] Equations subtyping_impl G A B (H : subtyping_order G A B) : { {{G ⊢a A ⊆ B}} } + { ~ {{ G ⊢a A ⊆ B }} } := | G, A, B, H => @@ -85,6 +89,8 @@ Next Obligation. contradiction. Qed. +(** Similar for [subtyping_impl]. *) + Theorem subtyping_impl_complete' : forall G A B, {{G ⊢a A ⊆ B}} -> forall (H : subtyping_order G A B), diff --git a/theories/Extraction/TypeCheck.v b/theories/Extraction/TypeCheck.v index cec25457..c203851c 100644 --- a/theories/Extraction/TypeCheck.v +++ b/theories/Extraction/TypeCheck.v @@ -45,12 +45,15 @@ Section lookup. End lookup. Section type_check. + #[derive(equations=no,eliminator=no)] Equations get_level_of_type_nf (A : nf) : { i | A = n{{{ Type@i }}} } + { forall i, A <> n{{{ Type@i }}} } := | n{{{ Type@i }}} => pureo (exist _ i _) | _ => inright _ . - (* Don't forget to use 9th bit of [Extraction Flag] (for example, [Set Extraction Flag 1007.]) *) + (** Don't forget to use 9th bit of [Extraction Flag] (for example, [Set Extraction Flag 1007.]). + Otherwise, this function would introduce redundant pair construction/pattern matching. *) + #[derive(equations=no,eliminator=no)] Equations get_subterms_of_pi_nf (A : nf) : { B & { C | A = n{{{ Π B C }}} } } + { forall B C, A <> n{{{ Π B C }}} } := | n{{{ Π B C }}} => pureo (existT _ B (exist _ C _)) | _ => inright _ @@ -336,8 +339,48 @@ Section type_check. Qed. Extraction Inline type_check_functional type_infer_functional. + + Lemma type_infer_order_soundness : forall G M A, + {{ G ⊢a M ⟹ A }} -> + type_infer_order M + with type_check_order_soundness : forall G M A, + {{ G ⊢a M ⟸ A }} -> + type_check_order M. + Proof. + - clear type_infer_order_soundness. + induction 1; mauto 3. + econstructor; mauto 3. + - clear type_check_order_soundness. + induction 1; mauto 3. + Qed. End type_check. +#[local] +Hint Resolve type_check_order_soundness type_infer_order_soundness : mcltt. + +Lemma type_check_complete' : forall G M A (HA : exists i, {{ G ⊢ A : Type@i }}), + {{ G ⊢a M ⟸ A }} -> + exists H H', type_check G A HA M H = left H'. +Proof. + intros ? ? ? [] ?. + assert (Horder : type_check_order M) by mauto. + exists Horder. + dec_complete. +Qed. + +Lemma type_infer_complete : forall G M A (HG : {{ ⊢ G }}), + {{ G ⊢a M ⟹ A }} -> + exists H H', type_infer G HG M H = inleft (exist _ A H'). +Proof. + intros. + assert (Horder : type_infer_order M) by mauto. + exists Horder. + destruct (type_infer G HG M Horder) as [[? []] |]. + - functional_alg_type_infer_rewrite_clear. + eexists; reflexivity. + - contradict H; intuition. +Qed. + Section type_check_closed. #[local] Ltac impl_obl_tac := @@ -378,3 +421,8 @@ Section type_check_closed. mauto 3 using alg_type_check_sound. Qed. End type_check_closed. + +Lemma type_check_closed_complete : forall A (HA : user_exp A) M (HM : user_exp M), + {{ ⋅ ⊢ M : A }} -> + exists H', type_check_closed A HA M HM = left H'. +Proof. intros; dec_complete. Qed. diff --git a/theories/Frontend/parserMessages.messages b/theories/Frontend/parserMessages.messages index d3002c11..2599b992 100644 --- a/theories/Frontend/parserMessages.messages +++ b/theories/Frontend/parserMessages.messages @@ -461,4 +461,4 @@ prog: INT COLON RPAREN ## An expression is expected. -This token is invalid for the beginning of an expression. \ No newline at end of file +This token is invalid for the beginning of an expression. diff --git a/theories/LibTactics.v b/theories/LibTactics.v index af0b538d..4944dbf2 100644 --- a/theories/LibTactics.v +++ b/theories/LibTactics.v @@ -229,22 +229,16 @@ Tactic Notation "mautosolve" int_or_var(pow) := mautosolve_impl pow. (* Improve type class resolution *) #[export] - Hint Extern 1 => eassumption : typeclass_instances. - -Ltac predicate_resolve := - lazymatch goal with - | |- @Reflexive _ (@predicate_equivalence _) => - simple apply @Equivalence_Reflexive - | |- @Symmetric _ (@predicate_equivalence _) => - simple apply @Equivalence_Symmetric - | |- @Transitive _ (@predicate_equivalence _) => - simple apply @Equivalence_Transitive - | |- @Transitive _ (@predicate_implication _) => - simple apply @PreOrder_Transitive - end. +Hint Extern 1 => eassumption : typeclass_instances. #[export] -Hint Extern 1 => predicate_resolve : typeclass_instances. +Hint Extern 1 (@Reflexive _ (@predicate_equivalence _)) => simple apply @Equivalence_Reflexive : typeclass_instances. +#[export] +Hint Extern 1 (@Symmetric _ (@predicate_equivalence _)) => simple apply @Equivalence_Symmetric : typeclass_instances. +#[export] +Hint Extern 1 (@Transitive _ (@predicate_equivalence _)) => simple apply @Equivalence_Transitive : typeclass_instances. +#[export] +Hint Extern 1 (@Transitive _ (@predicate_implication _)) => simple apply @PreOrder_Transitive : typeclass_instances. (* intuition tactic default setting *)