diff --git a/CHANGELOG.md b/CHANGELOG.md index 72740c033b..070c866edc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ - Analysis refactor: remove global state `Shared_types.state`. https://github.com/rescript-lang/rescript/pull/8465 - Refactor analysis CLI helpers to use source input. https://github.com/rescript-lang/rescript/pull/8466 - Include syntax, gentype, analysis, tools, and reanalyze tests in coverage reports. https://github.com/rescript-lang/rescript/pull/8467 +- Remove the unreachable `Longident.Lapply` constructor (OCaml's applicative-functor path syntax `F(X).t`, which ReScript's grammar cannot produce). https://github.com/rescript-lang/rescript/pull/8469 # 13.0.0-alpha.4 diff --git a/analysis/src/completion_front_end.ml b/analysis/src/completion_front_end.ml index 481c7a0d3f..aeaf765790 100644 --- a/analysis/src/completion_front_end.ml +++ b/analysis/src/completion_front_end.ml @@ -1322,7 +1322,6 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file } in set_result (Cpath context_path) - | Lapply _ -> () else if Loc.end_ e.pexp_loc = pos_before_cursor then match expr_to_context_path ~in_jsx_context:!in_jsx_context e with | Some context_path -> diff --git a/analysis/src/process_extra.ml b/analysis/src/process_extra.ml index 0c44d934f3..fcd5c8e1f1 100644 --- a/analysis/src/process_extra.ml +++ b/analysis/src/process_extra.ml @@ -81,11 +81,9 @@ let handle_constructor txt = match txt with | Longident.Lident name -> name | Ldot (_left, name) -> name - | Lapply (_, _) -> assert false let rec lid_is_complex (lid : Longident.t) = match lid with - | Lapply _ -> true | Ldot (lid, _) -> lid_is_complex lid | _ -> false diff --git a/analysis/src/semantic_tokens.ml b/analysis/src/semantic_tokens.ml index 10532f81ee..92b3afe836 100644 --- a/analysis/src/semantic_tokens.ml +++ b/analysis/src/semantic_tokens.ml @@ -127,7 +127,6 @@ let emit_longident ?(backwards = false) ?(jsx = false) match lid with | Longident.Lident txt -> txt :: acc | Ldot (lid, txt) -> flatten (txt :: acc) lid - | _ -> acc in let rec loop pos segments = match segments with diff --git a/analysis/src/utils.ml b/analysis/src/utils.ml index 18a26e3241..94736c41c2 100644 --- a/analysis/src/utils.ml +++ b/analysis/src/utils.ml @@ -77,7 +77,6 @@ let flatten_long_ident ?(jsx = false) ?(cut_at_offset = None) lid = else if jsx && txt = "createElement" then (path, offset) else if txt = "_" then (extend_path "" path, offset + 1) else (extend_path txt path, offset + 1 + String.length txt) - | Lapply _ -> ([], 0) in let path, _ = loop lid in List.rev path @@ -169,7 +168,6 @@ let rec get_unqualified_name txt = match txt with | Longident.Lident field_name -> field_name | Ldot (t, _) -> get_unqualified_name t - | _ -> "" let indent n text = let spaces = String.make n ' ' in diff --git a/compiler/frontend/ast_util.ml b/compiler/frontend/ast_util.ml index f5814164fa..798b6f4717 100644 --- a/compiler/frontend/ast_util.ml +++ b/compiler/frontend/ast_util.ml @@ -36,7 +36,7 @@ let record_as_js_object loc (self : Bs_ast_mapper.mapper) ( {Asttypes.loc; txt = x} :: labels, (x, self.expr self e) :: args, i + 1 ) - | Ldot _ | Lapply _ -> Location.raise_errorf ~loc "invalid js label ") + | Ldot _ -> Location.raise_errorf ~loc "invalid js label ") in Ast_external_mk.local_external_obj loc ~pval_prim:(Ast_external_process.pval_prim_of_labels labels) diff --git a/compiler/ml/ctype.ml b/compiler/ml/ctype.ml index 32deb499be..b07920b649 100644 --- a/compiler/ml/ctype.ml +++ b/compiler/ml/ctype.ml @@ -2113,7 +2113,6 @@ let rec concat_longident lid1 = function | Lident s -> Ldot (lid1, s) | Ldot (lid2, s) -> Ldot (concat_longident lid1 lid2, s) - | Lapply (lid2, lid) -> Lapply (concat_longident lid1 lid2, lid) let nondep_instance env level id ty = let ty = !nondep_type' env id ty in @@ -3353,8 +3352,12 @@ let memq_warn t visited = let rec lid_of_path ?(hash = "") = function | Path.Pident id -> Longident.Lident (hash ^ Ident.name id) | Path.Pdot (p1, s, _) -> Longident.Ldot (lid_of_path p1, hash ^ s) - | Path.Papply (p1, p2) -> - Longident.Lapply (lid_of_path ~hash p1, lid_of_path p2) + (* Applicative-functor application paths (Path.Papply) are produced + internally (applicative functors are on by default) even though ReScript's + surface syntax cannot reference such a path directly. Render the + application by name (e.g. "F(Arg)") rather than aborting, so any diagnostic + that reaches here degrades gracefully instead of crashing the compiler. *) + | Path.Papply _ as p -> Longident.Lident (hash ^ Path.name p) let find_cltype_for_path env p = let cl_path = Env.lookup_type (lid_of_path ~hash:"#" p) env in diff --git a/compiler/ml/depend.ml b/compiler/ml/depend.ml index 3b00ff9e5a..3a13d4ac00 100644 --- a/compiler/ml/depend.ml +++ b/compiler/ml/depend.ml @@ -55,7 +55,6 @@ let rec lookup_map lid m = match lid with | Lident s -> String_map.find s m | Ldot (l, s) -> String_map.find s (get_map (lookup_map l m)) - | Lapply _ -> raise Not_found (* Collect free module identifiers in the a.s.t. *) @@ -73,9 +72,6 @@ let rec add_path bv ?(p = []) = function prerr_endline "";*) add_names free | Ldot (l, s) -> add_path bv ~p:(s :: p) l - | Lapply (l1, l2) -> - add_path bv l1; - add_path bv l2 let open_module bv lid = match lookup_map lid bv with diff --git a/compiler/ml/env.ml b/compiler/ml/env.ml index 0626ba5552..a64f79185f 100644 --- a/compiler/ml/env.ml +++ b/compiler/ml/env.ml @@ -432,7 +432,6 @@ and structure_components = { and functor_components = { fcomp_param: Ident.t; (* Formal parameter *) - fcomp_arg: module_type option; (* Argument signature *) fcomp_res: module_type; (* Result signature *) fcomp_cache: (Path.t, module_components) Hashtbl.t; (* For memoization *) fcomp_subst_cache: (Path.t, module_type) Hashtbl.t; @@ -1013,20 +1012,6 @@ let rec lookup_module_descr_aux ?loc lid env = let descr, pos = Tbl.find_str s c.comp_components in (Pdot (p, s, pos), descr) | Functor_comps _ -> raise Not_found) - | Lapply (l1, l2) -> ( - let p1, desc1 = lookup_module_descr ?loc l1 env in - let p2 = lookup_module ~load:true ?loc l2 env in - let {md_type = mty2} = find_module p2 env in - match get_components desc1 with - | Functor_comps f -> - let loc = - match loc with - | Some l -> l - | None -> Location.none - in - Misc.may (!check_modtype_inclusion ~loc env mty2 p2) f.fcomp_arg; - (Papply (p1, p2), !components_of_functor_appl' f env p1 p2) - | Structure_comps _ -> raise Not_found) and lookup_module_descr ?loc lid env = let ((p, comps) as res) = lookup_module_descr_aux ?loc lid env in @@ -1073,21 +1058,6 @@ and lookup_module ~load ?loc lid env : Path.t = report_deprecated ?loc p comps.deprecated; p | Functor_comps _ -> raise Not_found) - | Lapply (l1, l2) -> ( - let p1, desc1 = lookup_module_descr ?loc l1 env in - let p2 = lookup_module ~load:true ?loc l2 env in - let {md_type = mty2} = find_module p2 env in - let p = Papply (p1, p2) in - match get_components desc1 with - | Functor_comps f -> - let loc = - match loc with - | Some l -> l - | None -> Location.none - in - Misc.may (!check_modtype_inclusion ~loc env mty2 p2) f.fcomp_arg; - p - | Structure_comps _ -> raise Not_found) let lookup proj1 proj2 ?loc lid env = match lid with @@ -1099,7 +1069,6 @@ let lookup proj1 proj2 ?loc lid env = let data, pos = Tbl.find_str s (proj2 c) in (Pdot (p, s, pos), data) | Functor_comps _ -> raise Not_found) - | Lapply _ -> raise Not_found let lookup_all_simple proj1 proj2 shadow ?loc lid env = match lid with @@ -1119,7 +1088,6 @@ let lookup_all_simple proj1 proj2 shadow ?loc lid env = let comps = try Tbl.find_str s (proj2 c) with Not_found -> [] in List.map (fun data -> (data, fun () -> ())) comps | Functor_comps _ -> raise Not_found) - | Lapply _ -> raise Not_found let has_local_constraints env = not (Path_map.is_empty env.local_constraints) @@ -1588,14 +1556,13 @@ and components_of_module_maker (env, sub, path, mty) = | Sig_class_type () -> assert false) sg pl; Some (Structure_comps c) - | Mty_functor (param, ty_arg, ty_res) -> + | Mty_functor (param, _ty_arg, ty_res) -> Some (Functor_comps { fcomp_param = param; - (* fcomp_arg and fcomp_res must be prefixed eagerly, because - they are interpreted in the outer environment *) - fcomp_arg = may_map (Subst.modtype sub) ty_arg; + (* fcomp_res must be prefixed eagerly, because it is interpreted + in the outer environment *) fcomp_res = Subst.modtype sub ty_res; fcomp_cache = Hashtbl.create 17; fcomp_subst_cache = Hashtbl.create 17; diff --git a/compiler/ml/longident.ml b/compiler/ml/longident.ml index 721a131055..3ea191d3ed 100644 --- a/compiler/ml/longident.ml +++ b/compiler/ml/longident.ml @@ -13,7 +13,7 @@ (* *) (**************************************************************************) -type t = Lident of string | Ldot of t * string | Lapply of t * t +type t = Lident of string | Ldot of t * string let rec cmp : t -> t -> int = fun a b -> if a == b then 0 @@ -26,24 +26,16 @@ let rec cmp : t -> t -> int = match cmp a c with | 0 -> compare b d | n -> n) - | Ldot _, _ -> -1 - | _, Ldot _ -> 1 - | Lapply (a, b), Lapply (c, d) -> ( - match cmp a c with - | 0 -> cmp b d - | n -> n) let rec flat accu = function | Lident s -> s :: accu | Ldot (lid, s) -> flat (s :: accu) lid - | Lapply (_, _) -> Misc.fatal_error "Longident.flat" let flatten lid = flat [] lid let last = function | Lident s -> s | Ldot (_, s) -> s - | Lapply (_, _) -> Misc.fatal_error "Longident.last" let rec split_at_dots s pos = try diff --git a/compiler/ml/longident.mli b/compiler/ml/longident.mli index 26ed938e84..51ddc2746c 100644 --- a/compiler/ml/longident.mli +++ b/compiler/ml/longident.mli @@ -15,7 +15,7 @@ (** Long identifiers, used in parsetree. *) -type t = Lident of string | Ldot of t * string | Lapply of t * t +type t = Lident of string | Ldot of t * string val cmp : t -> t -> int val flatten : t -> string list diff --git a/compiler/ml/pprintast.ml b/compiler/ml/pprintast.ml index a2f0fd4530..10025b0e0e 100644 --- a/compiler/ml/pprintast.ml +++ b/compiler/ml/pprintast.ml @@ -238,7 +238,6 @@ let paren : let rec longident f = function | Lident s -> protect_ident f s | Ldot (y, s) -> protect_longident f longident y s - | Lapply (y, s) -> pp f "%a(%a)" longident y longident s let longident_loc f x = pp f "%a" longident x.txt diff --git a/compiler/ml/printast.ml b/compiler/ml/printast.ml index f583acef64..5aae826373 100644 --- a/compiler/ml/printast.ml +++ b/compiler/ml/printast.ml @@ -36,8 +36,6 @@ let rec fmt_longident_aux f x = match x with | Longident.Lident s -> fprintf f "%s" s | Longident.Ldot (y, s) -> fprintf f "%a.%s" fmt_longident_aux y s - | Longident.Lapply (y, z) -> - fprintf f "%a(%a)" fmt_longident_aux y fmt_longident_aux z let fmt_longident_loc f (x : Longident.t loc) = fprintf f "\"%a\" %a" fmt_longident_aux x.txt fmt_location x.loc diff --git a/compiler/ml/printtyp.ml b/compiler/ml/printtyp.ml index da6a3b25b1..6dabdb5f61 100644 --- a/compiler/ml/printtyp.ml +++ b/compiler/ml/printtyp.ml @@ -35,7 +35,6 @@ let print_res_poly_identifier : (string -> string) ref = let rec longident ppf = function | Lident s -> pp_print_string ppf s | Ldot (p, s) -> fprintf ppf "%a.%s" longident p s - | Lapply (p1, p2) -> fprintf ppf "%a(%a)" longident p1 longident p2 (* Print an identifier *) diff --git a/compiler/ml/printtyped.ml b/compiler/ml/printtyped.ml index 0c30f5eb1f..f8bfaa170f 100644 --- a/compiler/ml/printtyped.ml +++ b/compiler/ml/printtyped.ml @@ -34,8 +34,6 @@ let rec fmt_longident_aux f x = match x with | Longident.Lident s -> fprintf f "%s" s | Longident.Ldot (y, s) -> fprintf f "%a.%s" fmt_longident_aux y s - | Longident.Lapply (y, z) -> - fprintf f "%a(%a)" fmt_longident_aux y fmt_longident_aux z let fmt_longident f x = fprintf f "\"%a\"" fmt_longident_aux x.txt diff --git a/compiler/ml/typemod.ml b/compiler/ml/typemod.ml index 7abdb05d43..bca0a51198 100644 --- a/compiler/ml/typemod.ml +++ b/compiler/ml/typemod.ml @@ -1696,7 +1696,6 @@ let type_package env m p nl = let rec mkpath mp = function | Lident name -> Pdot (mp, name, nopos) | Ldot (m, name) -> Pdot (mkpath mp m, name, nopos) - | _ -> assert false in let tl' = List.map diff --git a/compiler/ml/typetexp.ml b/compiler/ml/typetexp.ml index afee8118be..4e8617b863 100644 --- a/compiler/ml/typetexp.ml +++ b/compiler/ml/typetexp.ml @@ -81,28 +81,7 @@ let rec narrow_unbound_lid_error : 'a. _ -> _ -> _ -> _ -> 'a = raise (Error (loc, env, Access_functor_as_structure mlid)) | Mty_alias (_, p) -> raise (Error (loc, env, Cannot_scrape_alias (mlid, p))) - | _ -> ()) - | Longident.Lapply (flid, mlid) -> ( - check_module flid; - let fmd = Env.find_module (Env.lookup_module ~load:true flid env) env in - (match Env.scrape_alias env fmd.md_type with - | Mty_signature _ -> - (* unreachable: this branch handles Longident.Lapply paths, but - Lapply has no construction site in compiler/syntax/src/ — ReScript's - type-level applicative-functor syntax (OCaml's M(X).t) isn't part - of the grammar *) - assert false - | Mty_alias (_, p) -> - raise (Error (loc, env, Cannot_scrape_alias (flid, p))) - | _ -> ()); - check_module mlid; - let mmd = Env.find_module (Env.lookup_module ~load:true mlid env) env in - match Env.scrape_alias env mmd.md_type with - | Mty_alias (_, p) -> - raise (Error (loc, env, Cannot_scrape_alias (mlid, p))) - | _ -> - (* unreachable: same Lapply path as above *) - assert false)); + | _ -> ())); raise (Error (loc, env, make_error lid)) let find_component (lookup : ?loc:_ -> _) make_error env loc lid = @@ -746,7 +725,6 @@ let super_spellcheck ppf fold env lid = Misc.spellcheck env name in match lid with - | Longident.Lapply _ -> false | Longident.Lident s -> did_you_mean ppf (fun _ -> choices None s) | Longident.Ldot (r, s) -> did_you_mean ppf (fun _ -> choices (Some r) s) @@ -756,7 +734,6 @@ let spellcheck ppf fold env lid = Misc.spellcheck env name in match lid with - | Longident.Lapply _ -> () | Longident.Lident s -> Misc.did_you_mean ppf (fun () -> choices ~path:None s) | Longident.Ldot (r, s) -> Misc.did_you_mean ppf (fun () -> choices ~path:(Some r) s) diff --git a/compiler/syntax/src/res_ast_debugger.ml b/compiler/syntax/src/res_ast_debugger.ml index ff5b1a42c1..6749355ea3 100644 --- a/compiler/syntax/src/res_ast_debugger.ml +++ b/compiler/syntax/src/res_ast_debugger.ml @@ -76,8 +76,6 @@ module Sexp_ast = struct | Longident.Lident ident -> Sexp.list [Sexp.atom "Lident"; string ident] | Longident.Ldot (lident, txt) -> Sexp.list [Sexp.atom "Ldot"; loop lident; string txt] - | Longident.Lapply (l1, l2) -> - Sexp.list [Sexp.atom "Lapply"; loop l1; loop l2] in Sexp.list [Sexp.atom "longident"; loop l] diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 1e9a3f5a73..6c47f99bfb 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -416,10 +416,6 @@ let print_listi ~get_loc ~nodes ~print ?(ignore_empty_lines = false) let rec print_longident_aux accu = function | Longident.Lident s -> Doc.text s :: accu | Ldot (lid, s) -> print_longident_aux (Doc.text s :: accu) lid - | Lapply (lid1, lid2) -> - let d1 = Doc.join ~sep:Doc.dot (print_longident_aux [] lid1) in - let d2 = Doc.join ~sep:Doc.dot (print_longident_aux [] lid2) in - Doc.concat [d1; Doc.lparen; d2; Doc.rparen] :: accu let print_longident = function | Longident.Lident txt -> Doc.text txt @@ -529,30 +525,22 @@ let pending_inline_record_definitions inline_record_definitions = else None) let print_lident l = - let flat_lid_opt lid = + let flat_lid lid = let rec flat accu = function - | Longident.Lident s -> Some (s :: accu) + | Longident.Lident s -> s :: accu | Ldot (lid, s) -> flat (s :: accu) lid - | Lapply (_, _) -> None in flat [] lid in match l with | Longident.Lident txt -> print_ident_like txt | Longident.Ldot (path, txt) -> - let doc = - match flat_lid_opt path with - | Some txts -> - Doc.concat - [ - Doc.join ~sep:Doc.dot (List.map Doc.text txts); - Doc.dot; - print_ident_like txt; - ] - | None -> Doc.text "printLident: Longident.Lapply is not supported" - in - doc - | Lapply (_, _) -> Doc.text "printLident: Longident.Lapply is not supported" + Doc.concat + [ + Doc.join ~sep:Doc.dot (List.map Doc.text (flat_lid path)); + Doc.dot; + print_ident_like txt; + ] let print_longident_location l cmt_tbl = let doc = print_longident l.Location.txt in diff --git a/tests/build_tests/super_errors/expected/functor_application_type_mismatch.res.expected b/tests/build_tests/super_errors/expected/functor_application_type_mismatch.res.expected new file mode 100644 index 0000000000..9341a4e475 --- /dev/null +++ b/tests/build_tests/super_errors/expected/functor_application_type_mismatch.res.expected @@ -0,0 +1,14 @@ + + We've found a bug for you! + /.../fixtures/functor_application_type_mismatch.res:20:18-29 + + 18 │ // confirming neither hits the assert false left in Ctype.lid_of_path a + │ fter + 19 │ // removing the unreachable Longident.Lapply constructor. + 20 │ let bad: App.t = "not an int" + 21 │ + + This has type: string + But it's expected to have type: App.t (defined as int) + + You can convert string to int with Int.fromString. \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/functor_application_type_mismatch.res b/tests/build_tests/super_errors/fixtures/functor_application_type_mismatch.res new file mode 100644 index 0000000000..7d7cda3460 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/functor_application_type_mismatch.res @@ -0,0 +1,20 @@ +module type S = { + type t +} + +module F = (X: S) => { + type t = X.t + let make = (x: X.t): X.t => x +} + +module IntS = { + type t = int +} + +module App = F(IntS) + +// Type error on a value whose type lives behind a functor-application path. +// Exercises functor application (Path.Papply) and type-path error printing, +// confirming neither hits the assert false left in Ctype.lid_of_path after +// removing the unreachable Longident.Lapply constructor. +let bad: App.t = "not an int" diff --git a/tests/ounit_tests/ounit_lid_of_path_tests.ml b/tests/ounit_tests/ounit_lid_of_path_tests.ml new file mode 100644 index 0000000000..f9767631d5 --- /dev/null +++ b/tests/ounit_tests/ounit_lid_of_path_tests.ml @@ -0,0 +1,22 @@ +let ( >:: ), ( >::: ) = OUnit.(( >:: ), ( >::: )) +let ( =~ ) = OUnit.assert_equal + +(* [Ctype.lid_of_path] must render applicative-functor application paths + ([Path.Papply]) gracefully rather than aborting. Such paths are produced + internally (applicative functors are on by default), but ReScript source + cannot reference one, so the branch is unreachable end-to-end and is + exercised here directly with a synthetic path. *) +let papply () = + Path.Papply (Path.Pident (Ident.create "F"), Path.Pident (Ident.create "Arg")) + +let suites = + __FILE__ + >::: [ + (* F(Arg) renders to a single Lident, no crash *) + ( __LOC__ >:: fun _ -> + Ctype.lid_of_path (papply ()) =~ Longident.Lident "F(Arg)" ); + (* F(Arg).t renders to Ldot of that Lident *) + ( __LOC__ >:: fun _ -> + Ctype.lid_of_path (Path.Pdot (papply (), "t", 0)) + =~ Longident.Ldot (Longident.Lident "F(Arg)", "t") ); + ] diff --git a/tests/ounit_tests/ounit_tests_main.ml b/tests/ounit_tests/ounit_tests_main.ml index 7147844c6e..aefc3f3abe 100644 --- a/tests/ounit_tests/ounit_tests_main.ml +++ b/tests/ounit_tests/ounit_tests_main.ml @@ -14,6 +14,7 @@ let suites = Ounit_string_tests.suites; Ounit_int_vec_tests.suites; Ounit_ident_mask_tests.suites; + Ounit_lid_of_path_tests.suites; Ounit_utf8_test.suites; Ounit_unicode_tests.suites; Ounit_util_tests.suites;