diff options
Diffstat (limited to 'typing')
-rw-r--r-- | typing/ctype.ml | 190 | ||||
-rw-r--r-- | typing/ctype.mli | 12 | ||||
-rw-r--r-- | typing/parmatch.ml | 43 | ||||
-rw-r--r-- | typing/printtyp.ml | 6 | ||||
-rw-r--r-- | typing/printtyp.mli | 1 | ||||
-rw-r--r-- | typing/typeclass.ml | 26 | ||||
-rw-r--r-- | typing/typeclass.mli | 6 | ||||
-rw-r--r-- | typing/typecore.ml | 492 | ||||
-rw-r--r-- | typing/typecore.mli | 7 | ||||
-rw-r--r-- | typing/typedtree.mli | 2 | ||||
-rw-r--r-- | typing/typemod.ml | 75 | ||||
-rw-r--r-- | typing/typemod.mli | 5 | ||||
-rw-r--r-- | typing/typetexp.ml | 6 | ||||
-rw-r--r-- | typing/typetexp.mli | 1 |
14 files changed, 541 insertions, 331 deletions
diff --git a/typing/ctype.ml b/typing/ctype.ml index 8cebcf1d68..bec19ae528 100644 --- a/typing/ctype.ml +++ b/typing/ctype.ml @@ -307,6 +307,11 @@ let can_assume_injective () = | Expression | Subst -> false | Pattern { assume_injective } -> assume_injective +let in_counterexample () = + match !umode with + | Expression | Subst -> false + | Pattern { allow_recursive_equations } -> allow_recursive_equations + let allow_recursive_equations () = !Clflags.recursive_types || match !umode with @@ -1210,20 +1215,22 @@ let instance_list schl = For_copy.with_scope (fun copy_scope -> List.map (fun t -> copy copy_scope t) schl) -let reified_var_counter = ref Vars.empty -let reset_reified_var_counter () = - reified_var_counter := Vars.empty - -(* names given to new type constructors. - Used for existential types and - local constraints *) -let get_new_abstract_name s = - let index = - try Vars.find s !reified_var_counter + 1 - with Not_found -> 0 in - reified_var_counter := Vars.add s index !reified_var_counter; - if index = 0 && s <> "" && s.[String.length s - 1] <> '$' then s else - Printf.sprintf "%s%d" s index +(* Create unique names to new type constructors. + Used for existential types and local constraints. *) +let get_new_abstract_name env s = + (* unique names are needed only for error messages *) + if in_counterexample () then s else + let name index = + if index = 0 && s <> "" && s.[String.length s - 1] <> '$' then s else + Printf.sprintf "%s%d" s index + in + let check index = + match Env.find_type_by_name (Longident.Lident (name index)) env with + | _ -> false + | exception Not_found -> true + in + let index = Misc.find_first_mono check in + name index let new_local_type ?(loc = Location.none) ?manifest_and_scope () = let manifest, expansion_scope = @@ -1267,7 +1274,7 @@ let instance_constructor existential_treatment cstr = let decl = new_local_type () in let name = existential_name cstr existential in let (id, new_env) = - Env.enter_type (get_new_abstract_name name) decl !env + Env.enter_type (get_new_abstract_name !env name) decl !env ~scope:fresh_constr_scope in env := new_env; let to_unify = newty (Tconstr (Path.Pident id,[],ref Mnil)) in @@ -2163,7 +2170,7 @@ let reify env t = let name = match name with Some s -> "$'"^s | _ -> "$" in let decl = new_local_type () in let (id, new_env) = - Env.enter_type (get_new_abstract_name name) decl !env + Env.enter_type (get_new_abstract_name !env name) decl !env ~scope:fresh_constr_scope in let path = Path.Pident id in let t = newty2 ~level:lev (Tconstr (path,[],ref Mnil)) in @@ -5038,69 +5045,108 @@ let rec arity ty = | _ -> 0 (* Check for non-generalizable type variables *) -exception Nongen -let visited = ref TypeSet.empty - -let rec nongen_schema_rec env ty = - if TypeSet.mem ty !visited then () else begin - visited := TypeSet.add ty !visited; - match get_desc ty with - Tvar _ when get_level ty <> generic_level -> - raise Nongen - | Tconstr _ -> - let old = !visited in - begin try iter_type_expr (nongen_schema_rec env) ty - with Nongen -> try - visited := old; - nongen_schema_rec env (try_expand_head try_expand_safe env ty) - with Cannot_expand -> - raise Nongen - end - | Tfield(_, kind, t1, t2) -> - if field_kind_repr kind = Fpublic then - nongen_schema_rec env t1; - nongen_schema_rec env t2 - | Tvariant row -> - iter_row (nongen_schema_rec env) row; - if not (static_row row) then nongen_schema_rec env (row_more row) - | _ -> - iter_type_expr (nongen_schema_rec env) ty - end +let add_nongen_vars_in_schema = + let rec loop env ((visited, weak_set) as acc) ty = + if TypeSet.mem ty visited + then acc + else begin + let visited = TypeSet.add ty visited in + match get_desc ty with + | Tvar _ when get_level ty <> generic_level -> + visited, TypeSet.add ty weak_set + | Tconstr _ -> + let (_, unexpanded_candidate) as unexpanded_candidate' = + fold_type_expr + (loop env) + (visited, weak_set) + ty + in + (* Using `==` is okay because `loop` will return the original set + when it does not change it. Similarly, `TypeSet.add` will return + the original set if the element is already present. *) + if unexpanded_candidate == weak_set + then (visited, weak_set) + else begin + match + loop env (visited, weak_set) + (try_expand_head try_expand_safe env ty) + with + | exception Cannot_expand -> unexpanded_candidate' + | expanded_result -> expanded_result + end + | Tfield(_, kind, t1, t2) -> + let visited, weak_set = + match field_kind_repr kind with + | Fpublic -> loop env (visited, weak_set) t1 + | _ -> visited, weak_set + in + loop env (visited, weak_set) t2 + | Tvariant row -> + let visited, weak_set = + fold_row (loop env) (visited, weak_set) row + in + if not (static_row row) + then loop env (visited, weak_set) (row_more row) + else (visited, weak_set) + | _ -> + fold_type_expr (loop env) (visited, weak_set) ty + end + in + fun env acc ty -> + let _, result = loop env (TypeSet.empty, acc) ty in + result -(* Return whether all variables of type [ty] are generic. *) -let nongen_schema env ty = - visited := TypeSet.empty; - try - nongen_schema_rec env ty; - visited := TypeSet.empty; - false - with Nongen -> - visited := TypeSet.empty; - true +(* Return all non-generic variables of [ty]. *) +let nongen_vars_in_schema env ty = + let result = add_nongen_vars_in_schema env TypeSet.empty ty in + if TypeSet.is_empty result + then None + else Some result (* Check that all type variables are generalizable *) (* Use Env.empty to prevent expansion of recursively defined object types; cf. typing-poly/poly.ml *) -let rec nongen_class_type = function - | Cty_constr (_, params, _) -> - List.exists (nongen_schema Env.empty) params - | Cty_signature sign -> - nongen_schema Env.empty sign.csig_self - || nongen_schema Env.empty sign.csig_self_row - || Meths.exists - (fun _ (_, _, ty) -> nongen_schema Env.empty ty) - sign.csig_meths - || Vars.exists - (fun _ (_, _, ty) -> nongen_schema Env.empty ty) - sign.csig_vars - | Cty_arrow (_, ty, cty) -> - nongen_schema Env.empty ty - || nongen_class_type cty +let nongen_class_type = + let add_nongen_vars_in_schema' ty weak_set = + add_nongen_vars_in_schema Env.empty weak_set ty + in + let add_nongen_vars_in_schema_fold fold m weak_set = + let f _key (_,_,ty) weak_set = + add_nongen_vars_in_schema Env.empty weak_set ty + in + fold f m weak_set + in + let rec nongen_class_type cty weak_set = + match cty with + | Cty_constr (_, params, _) -> + List.fold_left + (add_nongen_vars_in_schema Env.empty) + weak_set + params + | Cty_signature sign -> + weak_set + |> add_nongen_vars_in_schema' sign.csig_self + |> add_nongen_vars_in_schema' sign.csig_self_row + |> add_nongen_vars_in_schema_fold Meths.fold sign.csig_meths + |> add_nongen_vars_in_schema_fold Vars.fold sign.csig_vars + | Cty_arrow (_, ty, cty) -> + add_nongen_vars_in_schema' ty weak_set + |> nongen_class_type cty + in + nongen_class_type let nongen_class_declaration cty = - List.exists (nongen_schema Env.empty) cty.cty_params - || nongen_class_type cty.cty_type - + List.fold_left + (add_nongen_vars_in_schema Env.empty) + TypeSet.empty + cty.cty_params + |> nongen_class_type cty.cty_type + +let nongen_vars_in_class_declaration cty = + let result = nongen_class_declaration cty in + if TypeSet.is_empty result + then None + else Some result (* Normalize a type before printing, saving... *) (* Cannot use mark_type because deep_occur uses it too *) diff --git a/typing/ctype.mli b/typing/ctype.mli index 0806250ebd..d252f8a622 100644 --- a/typing/ctype.mli +++ b/typing/ctype.mli @@ -414,13 +414,12 @@ val nondep_cltype_declaration: val is_contractive: Env.t -> Path.t -> bool val normalize_type: type_expr -> unit -val nongen_schema: Env.t -> type_expr -> bool - (* Check whether the given type scheme contains no non-generic - type variables *) +val nongen_vars_in_schema: Env.t -> type_expr -> Btype.TypeSet.t option + (* Return any non-generic variables in the type scheme *) -val nongen_class_declaration: class_declaration -> bool - (* Check whether the given class type contains no non-generic - type variables. Uses the empty environment. *) +val nongen_vars_in_class_declaration:class_declaration -> Btype.TypeSet.t option + (* Return any non-generic variables in the class type. + Uses the empty environment. *) type variable_kind = Row_variable | Type_variable type closed_class_failure = { @@ -448,7 +447,6 @@ val collapse_conj_params: Env.t -> type_expr list -> unit val get_current_level: unit -> int val wrap_trace_gadt_instances: Env.t -> ('a -> 'b) -> 'a -> 'b -val reset_reified_var_counter: unit -> unit val immediacy : Env.t -> type_expr -> Type_immediacy.t diff --git a/typing/parmatch.ml b/typing/parmatch.ml index 138e9cea44..afb9f10777 100644 --- a/typing/parmatch.ml +++ b/typing/parmatch.ml @@ -2312,52 +2312,17 @@ let pattern_stable_vars ns p = (* All identifier paths that appear in an expression that occurs as a clause right hand side or guard. - - The function is rather complex due to the compilation of - unpack patterns by introducing code in rhs expressions - and **guards**. - - For pattern (module M:S) -> e the code is - let module M_mod = unpack M .. in e - - Hence M is "free" in e iff M_mod is free in e. - - Not doing so will yield excessive warning in - (module (M:S) } ...) when true -> .... - as M is always present in - let module M_mod = unpack M .. in true *) let all_rhs_idents exp = let ids = ref Ident.Set.empty in -(* Very hackish, detect unpack pattern compilation - and perform "indirect check for them" *) - let is_unpack exp = - List.exists - (fun attr -> attr.Parsetree.attr_name.txt = "#modulepat") - exp.exp_attributes in let open Tast_iterator in let expr_iter iter exp = - (match exp.exp_desc with - | Texp_ident (path, _lid, _descr) -> + match exp.exp_desc with + | Texp_ident (path, _lid, _descr) -> List.iter (fun id -> ids := Ident.Set.add id !ids) (Path.heads path) - (* Use default iterator methods for rest of match.*) - | _ -> Tast_iterator.default_iterator.expr iter exp); - - if is_unpack exp then begin match exp.exp_desc with - | Texp_letmodule - (id_mod,_,_, - {mod_desc= - Tmod_unpack ({exp_desc=Texp_ident (Path.Pident id_exp,_,_)},_)}, - _) -> - assert (Ident.Set.mem id_exp !ids) ; - begin match id_mod with - | Some id_mod when not (Ident.Set.mem id_mod !ids) -> - ids := Ident.Set.remove id_exp !ids - | _ -> () - end - | _ -> assert false - end + (* Use default iterator methods for rest of match.*) + | _ -> Tast_iterator.default_iterator.expr iter exp in let iterator = {Tast_iterator.default_iterator with expr = expr_iter} in iterator.expr iterator exp; diff --git a/typing/printtyp.ml b/typing/printtyp.ml index b14b4feab8..42b11a4d21 100644 --- a/typing/printtyp.ml +++ b/typing/printtyp.ml @@ -1123,7 +1123,7 @@ let rec tree_of_typexp mode ty = let tpath = tree_of_best_type_path p p' in Otyp_constr (tpath, tree_of_typlist mode tyl') | Tvariant row -> - let Row {fields; name; closed} = row_repr row in + let Row {fields; name; closed; _} = row_repr row in let fields = if closed then List.filter (fun (_, f) -> row_field_repr f <> Rabsent) @@ -1281,9 +1281,11 @@ let shared_type_scheme ppf ty = prepare_type ty; typexp Type_scheme ppf ty +let prepared_type_scheme ppf ty = typexp Type_scheme ppf ty + let type_scheme ppf ty = prepare_for_printing [ty]; - typexp Type_scheme ppf ty + prepared_type_scheme ppf ty let type_path ppf p = let (p', s) = best_type_path p in diff --git a/typing/printtyp.mli b/typing/printtyp.mli index eaa3599183..838a54f362 100644 --- a/typing/printtyp.mli +++ b/typing/printtyp.mli @@ -119,6 +119,7 @@ val prepared_type_expr: formatter -> type_expr -> unit val constructor_arguments: formatter -> constructor_arguments -> unit val tree_of_type_scheme: type_expr -> out_type val type_scheme: formatter -> type_expr -> unit +val prepared_type_scheme: formatter -> type_expr -> unit val shared_type_scheme: formatter -> type_expr -> unit (** [shared_type_scheme] is very similar to [type_scheme], but does not reset the printing context first. This is intended to be used in cases where the diff --git a/typing/typeclass.ml b/typing/typeclass.ml index 3b4c354266..9450e14fde 100644 --- a/typing/typeclass.ml +++ b/typing/typeclass.ml @@ -94,7 +94,11 @@ type error = | Class_match_failure of Ctype.class_match_failure list | Unbound_val of string | Unbound_type_var of (formatter -> unit) * Ctype.closed_class_failure - | Non_generalizable_class of Ident.t * Types.class_declaration + | Non_generalizable_class of + { id : Ident.t + ; clty : Types.class_declaration + ; nongen_vars : type_expr list + } | Cannot_coerce_self of type_expr | Non_collapsable_conjunction of Ident.t * Types.class_declaration * Errortrace.unification_error @@ -1171,7 +1175,7 @@ and class_expr_aux cl_num val_env met_env virt self_scope scl = in let partial = let dummy = type_exp val_env (Ast_helper.Exp.unreachable ()) in - Typecore.check_partial val_env pat.pat_type pat.pat_loc + Typecore.check_partial Modules_rejected val_env pat.pat_type pat.pat_loc [{c_lhs = pat; c_guard = None; c_rhs = dummy}] in let cl = @@ -1716,8 +1720,12 @@ let final_decl env define_class List.iter Ctype.generalize cl_abbr.type_params; Option.iter Ctype.generalize cl_abbr.type_manifest; - if Ctype.nongen_class_declaration clty then - raise(Error(cl.pci_loc, env, Non_generalizable_class (id, clty))); + Ctype.nongen_vars_in_class_declaration clty + |> Option.iter (fun vars -> + let nongen_vars = Btype.TypeSet.elements vars in + raise(Error(cl.pci_loc, env + , Non_generalizable_class { id; clty; nongen_vars })); + ); begin match Ctype.closed_class clty.cty_params @@ -2100,11 +2108,17 @@ let report_error env ppf = function "@[<v>@[Some type variables are unbound in this type:@;<1 2>%t@]@ \ @[%a@]@]" printer print_reason reason - | Non_generalizable_class (id, clty) -> + | Non_generalizable_class {id; clty; nongen_vars } -> + let[@manual.ref "ss:valuerestriction"] manual_ref = [ 6; 1; 2] in + Printtyp.prepare_for_printing nongen_vars; fprintf ppf "@[The type of this class,@ %a,@ \ - contains type variables that cannot be generalized@]" + contains the non-generalizable type variable(s): %a.@ %a@]" (Printtyp.class_declaration id) clty + (pp_print_list ~pp_sep:(fun f () -> fprintf f ",@ ") + Printtyp.prepared_type_scheme) nongen_vars + Misc.print_see_manual manual_ref + | Cannot_coerce_self ty -> fprintf ppf "@[The type of self cannot be coerced to@ \ diff --git a/typing/typeclass.mli b/typing/typeclass.mli index 4b6f734f8a..b83d56fc02 100644 --- a/typing/typeclass.mli +++ b/typing/typeclass.mli @@ -112,7 +112,11 @@ type error = | Class_match_failure of Ctype.class_match_failure list | Unbound_val of string | Unbound_type_var of (formatter -> unit) * Ctype.closed_class_failure - | Non_generalizable_class of Ident.t * Types.class_declaration + | Non_generalizable_class of + { id : Ident.t + ; clty : Types.class_declaration + ; nongen_vars : type_expr list + } | Cannot_coerce_self of type_expr | Non_collapsable_conjunction of Ident.t * Types.class_declaration * Errortrace.unification_error diff --git a/typing/typecore.ml b/typing/typecore.ml index 220382a1d7..16085f54ea 100644 --- a/typing/typecore.ml +++ b/typing/typecore.ml @@ -40,12 +40,6 @@ type type_expected = { explanation: type_forcing_context option; } -type to_unpack = { - tu_name: string Location.loc; - tu_loc: Location.t; - tu_uid: Uid.t -} - module Datatype_kind = struct type t = Record | Variant @@ -107,6 +101,7 @@ type error = | Apply_non_function of { funct : Typedtree.expression; func_ty : type_expr; + res_ty : type_expr; previous_arg_loc : Location.t; extra_arg_loc : Location.t; } @@ -231,6 +226,15 @@ type recarg = | Required | Rejected +(* Whether or not patterns of the form (module M) are accepted. (If they are, + the idents will be created at the provided scope.) When module patterns are + allowed, the caller should take care to check that the introduced module + bindings' types don't escape their scope; see the callsites in [type_let] + and [type_cases] for examples. +*) +type module_patterns_restriction = + | Modules_allowed of { scope : int } + | Modules_rejected let mk_expected ?explanation ty = { ty; explanation; } @@ -454,11 +458,16 @@ type pattern_variable = } type module_variable = - string loc * Location.t + { + mv_id: Ident.t; + mv_name: string Location.loc; + mv_loc: Location.t; + mv_uid: Uid.t + } let pattern_variables = ref ([] : pattern_variable list) let pattern_force = ref ([] : (unit -> unit) list) -let allow_modules = ref false +let allow_modules = ref Modules_rejected let module_variables = ref ([] : module_variable list) let reset_pattern allow = pattern_variables := []; @@ -482,19 +491,33 @@ let enter_variable ?(is_module=false) ?(is_as_variable=false) loc name ty if List.exists (fun {pv_id; _} -> Ident.name pv_id = name.txt) !pattern_variables then raise(Error(loc, Env.empty, Multiply_bound_variable name.txt)); - let id = Ident.create_local name.txt in + let id = + if is_module then begin + (* Unpack patterns result in both a module declaration and a value + variable of the same name being entered into the environment. (The + module is via [module_variables], and the variable is via + [pattern_variables].) *) + match !allow_modules with + | Modules_rejected -> + raise (Error (loc, Env.empty, Modules_not_allowed)); + | Modules_allowed { scope } -> + let id = Ident.create_scoped name.txt ~scope in + module_variables := + { mv_id = id; + mv_name = name; + mv_loc = loc; + mv_uid = Uid.mk ~current_unit:(Env.get_unit_name ()); + } :: !module_variables; + id + end else + Ident.create_local name.txt + in pattern_variables := {pv_id = id; pv_type = ty; pv_loc = loc; pv_as_var = is_as_variable; pv_attributes = attrs} :: !pattern_variables; - if is_module then begin - (* Note: unpack patterns enter a variable of the same name *) - if not !allow_modules then - raise (Error (loc, Env.empty, Modules_not_allowed)); - module_variables := (name, loc) :: !module_variables - end; id let sort_pattern_variables vs = @@ -1348,7 +1371,7 @@ type 'case_pattern half_typed_case = untyped_case: Parsetree.case; branch_env: Env.t; pat_vars: pattern_variable list; - unpacks: module_variable list; + module_vars: module_variable list; contains_gadt: bool; } let rec has_literal_pattern p = match p.ppat_desc with @@ -1502,6 +1525,9 @@ and type_pat_aux pat_env = !env } | Some s -> let v = { name with txt = s } in + (* We're able to pass ~is_module:true here without an error because + [Ppat_unpack] is a case identified by [may_contain_modules]. See + the comment on [may_contain_modules]. *) let id = enter_variable loc v t ~is_module:true sp.ppat_attributes in rvp { pat_desc = Tpat_var (id, v); @@ -1840,18 +1866,50 @@ let add_pattern_variables ?check ?check_as env pv = ) pv env -let type_pattern category ~lev env spat expected_ty = - reset_pattern true; +let add_module_variables env module_variables = + List.fold_left (fun env { mv_id; mv_loc; mv_name; mv_uid } -> + Typetexp.TyVarEnv.with_local_scope begin fun () -> + (* This code is parallel to the typing of Pexp_letmodule. However we + omit the call to [Mtype.lower_nongen] as it's not necessary here. + For Pexp_letmodule, the call to [type_module] is done in a raised + level and so needs to be modified to have the correct, outer level. + Here, on the other hand, we're calling [type_module] outside the + raised level, so there's no extra step to take. + *) + let modl, md_shape = + !type_module env + Ast_helper.( + Mod.unpack ~loc:mv_loc + (Exp.ident ~loc:mv_name.loc + (mkloc (Longident.Lident mv_name.txt) + mv_name.loc))) + in + let pres = + match modl.mod_type with + | Mty_alias _ -> Mp_absent + | _ -> Mp_present + in + let md = + { md_type = modl.mod_type; md_attributes = []; + md_loc = mv_name.loc; + md_uid = mv_uid; } + in + Env.add_module_declaration ~shape:md_shape ~check:true mv_id pres md env + end + ) env module_variables + +let type_pattern category ~lev env spat expected_ty allow_modules = + reset_pattern allow_modules; let new_env = ref env in let pat = type_pat category ~lev new_env spat expected_ty in let pvs = get_ref pattern_variables in - let unpacks = get_ref module_variables in - (pat, !new_env, get_ref pattern_force, pvs, unpacks) + let mvs = get_ref module_variables in + (pat, !new_env, get_ref pattern_force, pvs, mvs) let type_pattern_list - category no_existentials env spatl expected_tys allow + category no_existentials env spatl expected_tys allow_modules = - reset_pattern allow; + reset_pattern allow_modules; let new_env = ref env in let type_pat (attrs, pat) ty = Builtin_attributes.warning_scope ~ppwarning:false attrs @@ -1861,17 +1919,11 @@ let type_pattern_list in let patl = List.map2 type_pat spatl expected_tys in let pvs = get_ref pattern_variables in - let unpacks = - List.map (fun (name, loc) -> - {tu_name = name; tu_loc = loc; - tu_uid = Uid.mk ~current_unit:(Env.get_unit_name ())} - ) (get_ref module_variables) - in - let new_env = add_pattern_variables !new_env pvs in - (patl, new_env, get_ref pattern_force, pvs, unpacks) + let mvs = get_ref module_variables in + (patl, !new_env, get_ref pattern_force, pvs, mvs) let type_class_arg_pattern cl_num val_env met_env l spat = - reset_pattern false; + reset_pattern Modules_rejected; let nv = newvar () in let pat = type_pat Value ~no_existentials:In_class_args (ref val_env) spat nv in @@ -1918,7 +1970,7 @@ let type_class_arg_pattern cl_num val_env met_env l spat = let type_self_pattern env spat = let open Ast_helper in let spat = Pat.mk(Ppat_alias (spat, mknoloc "selfpat-*")) in - reset_pattern false; + reset_pattern Modules_rejected; let nv = newvar() in let pat = type_pat Value ~no_existentials:In_self_pattern (ref env) spat nv in @@ -2222,7 +2274,8 @@ let check_counter_example_pat ~counter_example_args (* this function is passed to Partial.parmatch to type check gadt nonexhaustiveness *) -let partial_pred ~lev ~splitting_mode ?(explode=0) env expected_ty p = +let partial_pred ~lev ~allow_modules ~splitting_mode ?(explode=0) + env expected_ty p = let env = ref env in let state = save_state env in let counter_example_args = @@ -2231,7 +2284,7 @@ let partial_pred ~lev ~splitting_mode ?(explode=0) env expected_ty p = explosion_fuel = explode; } in try - reset_pattern true; + reset_pattern allow_modules; let typed_p = check_counter_example_pat ~lev ~counter_example_args env p expected_ty in set_state state env; @@ -2241,17 +2294,22 @@ let partial_pred ~lev ~splitting_mode ?(explode=0) env expected_ty p = set_state state env; None -let check_partial ?(lev=get_current_level ()) env expected_ty loc cases = +let check_partial + ?(lev=get_current_level ()) allow_modules env expected_ty loc cases + = let explode = match cases with [_] -> 5 | _ -> 0 in let splitting_mode = Refine_or {inside_nonsplit_or = false} in Parmatch.check_partial - (partial_pred ~lev ~splitting_mode ~explode env expected_ty) loc cases + (partial_pred ~lev ~allow_modules ~splitting_mode ~explode env expected_ty) + loc cases -let check_unused ?(lev=get_current_level ()) env expected_ty cases = +let check_unused + ?(lev=get_current_level ()) allow_modules env expected_ty cases + = Parmatch.check_unused (fun refute pat -> match - partial_pred ~lev ~splitting_mode:Backtrack_or ~explode:5 + partial_pred ~lev ~allow_modules ~splitting_mode:Backtrack_or ~explode:5 env expected_ty pat with Some pat' when refute -> @@ -2655,6 +2713,19 @@ let check_partial_application ~statement exp = | _ -> doit () +let pattern_needs_partial_application_check p = + let rec check : type a. a general_pattern -> bool = fun p -> + not (List.exists (function (Tpat_constraint _, _, _) -> true | _ -> false) + p.pat_extra) && + match p.pat_desc with + | Tpat_any -> true + | Tpat_exception _ -> true + | Tpat_or (p1, p2, _) -> check p1 && check p2 + | Tpat_value p -> check (p :> value general_pattern) + | _ -> false + in + check p + (* Check that a type is generalizable at some level *) let generalizable level ty = let rec check ty = @@ -2738,6 +2809,22 @@ let may_contain_gadts p = | _ -> false) p +(* There are various things that we need to do in presence of module patterns + that aren't required if there are none. Most notably, we need to ensure the + modules are entered at the appropriate scope. The caller should use + [may_contain_modules] as an indication to set up the proper scope handling + code (via [allow_modules]) to permit module patterns. + The class of patterns identified here should stay in sync with the patterns + whose typing involves [enter_variable ~is_module:true], as these calls + will error if the scope handling isn't set up. +*) +let may_contain_modules p = + exists_ppat + (function + | {ppat_desc = Ppat_unpack _} -> true + | _ -> false) + p + let check_absent_variant env = iter_general_pattern { f = fun (type k) (pat : k general_pattern) -> match pat.pat_desc with @@ -2858,6 +2945,45 @@ let may_lower_contravariant_then_generalize env exp = if maybe_expansive exp then lower_contravariant env exp.exp_type; generalize exp.exp_type +(* value binding elaboration *) + +let vb_exp_constraint {pvb_expr=expr; pvb_pat=pat; pvb_constraint=ct; _ } = + let open Ast_helper in + match ct with + | None -> expr + | Some {locally_abstract_univars=[]; typ } -> + begin match typ.ptyp_desc with + | Ptyp_poly _ -> expr + | _ -> + let loc = { expr.pexp_loc with Location.loc_ghost = true } in + Exp.constraint_ ~loc expr typ + end + | Some {locally_abstract_univars;typ} -> + let loc_start = pat.ppat_loc.Location.loc_start in + let loc = { expr.pexp_loc with loc_start; loc_ghost=true } in + let expr = Exp.constraint_ ~loc expr typ in + List.fold_right (Exp.newtype ~loc) locally_abstract_univars expr + +let vb_pat_constraint ({pvb_pat=pat; pvb_expr = exp; _ } as vb) = + vb.pvb_attributes, + let open Ast_helper in + match vb.pvb_constraint, pat.ppat_desc, exp.pexp_desc with + | Some {locally_abstract_univars=[]; typ }, _, _ -> + Pat.constraint_ ~loc:{pat.ppat_loc with Location.loc_ghost=true} pat typ + | Some {locally_abstract_univars; typ }, _, _ -> + let varified = Typ.varify_constructors locally_abstract_univars typ in + let t = Typ.poly ~loc:typ.ptyp_loc locally_abstract_univars varified in + let loc_end = typ.ptyp_loc.Location.loc_end in + let loc = { pat.ppat_loc with loc_end; loc_ghost=true } in + Pat.constraint_ ~loc pat t + | None, (Ppat_any | Ppat_constraint _), _ -> pat + | None, _, Pexp_coerce (_, _, sty) + | None, _, Pexp_constraint (_, sty) when !Clflags.principal -> + (* propagate type annotation to pattern, + to allow it to be generalized in -principal mode *) + Pat.constraint_ ~loc:{pat.ppat_loc with Location.loc_ghost=true} pat sty + | _ -> pat + let rec type_exp ?recarg env sexp = (* We now delegate everything to type_expect *) type_expect ?recarg env sexp (mk_expected (newvar ())) @@ -2959,9 +3085,10 @@ and type_expect_ exp_attributes = sexp.pexp_attributes; exp_env = env } | Pexp_let(Nonrecursive, - [{pvb_pat=spat; pvb_expr=sval; pvb_attributes=[]}], sbody) + [{pvb_pat=spat; pvb_attributes=[]; _ } as vb], sbody) when may_contain_gadts spat -> - (* TODO: allow non-empty attributes? *) + (* TODO: allow non-empty attributes? *) + let sval = vb_exp_constraint vb in type_expect ?in_function env {sexp with pexp_desc = Pexp_match (sval, [Ast_helper.Exp.case spat sbody])} @@ -2971,12 +3098,64 @@ and type_expect_ if rec_flag = Recursive then In_rec else if List.compare_length_with spat_sexp_list 1 > 0 then In_group else With_attributes in - let (pat_exp_list, new_env, unpacks) = - type_let existential_context env rec_flag spat_sexp_list true in - let body = type_unpacks new_env unpacks sbody ty_expected_explained in - let () = - if rec_flag = Recursive then - check_recursive_bindings env pat_exp_list + let may_contain_modules = + List.exists (fun pvb -> may_contain_modules pvb.pvb_pat) spat_sexp_list + in + let outer_level = get_current_level () in + let (pat_exp_list, body, _new_env) = + (* If the patterns contain module unpacks, there is a possibility that + the types of the let body or bound expressions mention types + introduced by those unpacks. The below code checks for scope escape + via both of these pathways (body, bound expressions). + *) + with_local_level_if may_contain_modules begin fun () -> + let allow_modules = + if may_contain_modules + then + let scope = create_scope () in + Modules_allowed { scope } + else Modules_rejected + in + let (pat_exp_list, new_env) = + type_let existential_context env rec_flag spat_sexp_list + allow_modules + in + let body = type_expect new_env sbody ty_expected_explained in + let () = + if rec_flag = Recursive then + check_recursive_bindings env pat_exp_list + in + (* The "bound expressions" component of the scope escape check. + + This kind of scope escape is relevant only for recursive + module definitions. + *) + if rec_flag = Recursive && may_contain_modules then begin + List.iter + (fun vb -> + (* [type_let] already generalized bound expressions' types + in-place. We first take an instance before checking scope + escape at the outer level to avoid losing generality of + types added to [new_env]. + *) + let bound_exp = vb.vb_expr in + generalize_structure_exp bound_exp; + let bound_exp_type = Ctype.instance bound_exp.exp_type in + let loc = proper_exp_loc bound_exp in + let outer_var = newvar2 outer_level in + (* Checking unification within an environment extended with the + module bindings allows us to correctly accept more programs. + This environment allows unification to identify more cases + where a type introduced by the module is equal to a type + introduced at an outer scope. *) + unify_exp_types loc new_env bound_exp_type outer_var) + pat_exp_list + end; + (pat_exp_list, body, new_env) + end + ~post:(fun (_pat_exp_list, body, new_env) -> + (* The "body" component of the scope escape check. *) + unify_exp new_env body (newvar ())) in re { exp_desc = Texp_let(rec_flag, pat_exp_list, body); @@ -2988,12 +3167,13 @@ and type_expect_ assert(is_optional l); (* default allowed only with optional argument *) let open Ast_helper in let default_loc = default.pexp_loc in + let default_ghost = {default.pexp_loc with loc_ghost = true} in let scases = [ Exp.case - (Pat.construct ~loc:default_loc + (Pat.construct ~loc:default_ghost (mknoloc (Longident.(Ldot (Lident "*predef*", "Some")))) - (Some ([], Pat.var ~loc:default_loc (mknoloc "*sth*")))) - (Exp.ident ~loc:default_loc (mknoloc (Longident.Lident "*sth*"))); + (Some ([], Pat.var ~loc:default_ghost (mknoloc "*sth*")))) + (Exp.ident ~loc:default_ghost (mknoloc (Longident.Lident "*sth*"))); Exp.case (Pat.construct ~loc:default_loc @@ -3083,6 +3263,10 @@ and type_expect_ let cases, partial = type_cases Computation env arg.exp_type ty_expected_explained true loc caselist in + if + List.for_all (fun c -> pattern_needs_partial_application_check c.c_lhs) + cases + then check_partial_application ~statement:false arg; re { exp_desc = Texp_match(arg, cases, partial); exp_loc = loc; exp_extra = []; @@ -4556,6 +4740,7 @@ and type_application env funct sargs = raise(Error(funct.exp_loc, env, Apply_non_function { funct; func_ty = expand_head env funct.exp_type; + res_ty = expand_head env ty_res; previous_arg_loc; extra_arg_loc = sarg.pexp_loc; })) in @@ -4821,64 +5006,6 @@ and type_statement ?explanation env sexp = exp end -and type_unpacks ?(in_function : (Location.t * type_expr) option) - env (unpacks : to_unpack list) sbody expected_ty = - let ty = newvar() (* remember original level *) - and exp_loc = { sbody.pexp_loc with loc_ghost = true } - and exp_attributes = [Ast_helper.Attr.mk (mknoloc "#modulepat") (PStr [])] - in - let rec fold_unpacks env = function - | [] -> - (* ideally, we should catch Expr_type_clash errors - in type_expect triggered by escaping identifiers from the local - module and refine them into Scoping_let_module errors - *) - type_expect ?in_function env sbody expected_ty - | unpack :: rem -> - with_local_level begin fun () -> - let name, modl, pres, id, extended_env = - Typetexp.TyVarEnv.with_local_scope begin fun () -> - let name = unpack.tu_name in - let modl, md_shape = - !type_module env - Ast_helper.( - Mod.unpack ~loc:unpack.tu_loc - (Exp.ident ~loc:name.loc - (mkloc (Longident.Lident name.txt) name.loc))) - in - Mtype.lower_nongen (get_level ty) modl.mod_type; - let pres = - match modl.mod_type with - | Mty_alias _ -> Mp_absent - | _ -> Mp_present - in - let scope = create_scope () in - let md = - { md_type = modl.mod_type; md_attributes = []; - md_loc = name.loc; - md_uid = unpack.tu_uid; } - in - let (id, extended_env) = - Env.enter_module_declaration ~scope ~shape:md_shape - name.txt pres md env - in - name, modl, pres, id, extended_env - end - in - let body = fold_unpacks extended_env rem in - Ctype.unify_var extended_env ty body.exp_type; - re { - exp_desc = Texp_letmodule(Some id, { name with txt = Some name.txt }, - pres, modl, body); - exp_loc; - exp_attributes; - exp_extra = []; - exp_type = ty; - exp_env = env } - end - in - fold_unpacks env unpacks - (* Typing of match cases *) and type_cases : type k . k pattern_category -> @@ -4892,6 +5019,8 @@ and type_cases let contains_polyvars = List.exists contains_polymorphic_variant patterns in let erase_either = contains_polyvars && contains_variant_either ty_arg in let may_contain_gadts = List.exists may_contain_gadts patterns in + let may_contain_modules = List.exists may_contain_modules patterns in + let create_inner_level = may_contain_gadts || may_contain_modules in let ty_arg = if (may_contain_gadts || erase_either) && not !Clflags.principal then correct_levels ty_arg else ty_arg @@ -4908,8 +5037,18 @@ and type_cases | _ -> true in let outer_level = get_current_level () in - with_local_level_iter_if may_contain_gadts begin fun () -> + with_local_level_iter_if create_inner_level begin fun () -> let lev = get_current_level () in + let allow_modules = + if may_contain_modules + then + (* The corresponding check for scope escape is done together with + the check for GADT-induced existentials by + [with_local_level_iter_if create_inner_level]. + *) + Modules_allowed { scope = lev } + else Modules_rejected + in let take_partial_instance = if erase_either then Some false else None @@ -4930,8 +5069,8 @@ and type_cases with_local_level ~post:generalize_structure (fun () -> instance ?partial:take_partial_instance ty_arg) in - let (pat, ext_env, force, pvs, unpacks) = - type_pattern category ~lev env pc_lhs ty_arg + let (pat, ext_env, force, pvs, mvs) = + type_pattern category ~lev env pc_lhs ty_arg allow_modules in pattern_force := force @ !pattern_force; { typed_pat = pat; @@ -4939,7 +5078,7 @@ and type_cases untyped_case = case; branch_env = ext_env; pat_vars = pvs; - unpacks; + module_vars = mvs; contains_gadt = contains_gadt (as_comp_pattern category pat); } end ~post: begin fun htc -> @@ -4998,7 +5137,8 @@ and type_cases let ty_res' = instance ty_res in let cases = with_local_level_if_principal ~post:ignore begin fun () -> List.map - (fun { typed_pat = pat; branch_env = ext_env; pat_vars = pvs; unpacks; + (fun { typed_pat = pat; branch_env = ext_env; + pat_vars = pvs; module_vars = mvs; untyped_case = {pc_lhs = _; pc_guard; pc_rhs}; contains_gadt; _ } -> let ext_env = @@ -5012,12 +5152,7 @@ and type_cases ~check:(fun s -> Warnings.Unused_var_strict s) ~check_as:(fun s -> Warnings.Unused_var s) in - let unpacks = - List.map (fun (name, loc) -> - {tu_name = name; tu_loc = loc; - tu_uid = Uid.mk ~current_unit:(Env.get_unit_name ())} - ) unpacks - in + let ext_env = add_module_variables ext_env mvs in let ty_expected = if contains_gadt && not !Clflags.principal then (* Take a generic copy of [ty_res] again to allow propagation of @@ -5029,12 +5164,12 @@ and type_cases | None -> None | Some scond -> Some - (type_unpacks ext_env unpacks scond + (type_expect ext_env scond (mk_expected ~explanation:When_guard Predef.type_bool)) in let exp = - type_unpacks ?in_function ext_env - unpacks pc_rhs (mk_expected ?explanation ty_expected) + type_expect ?in_function ext_env + pc_rhs (mk_expected ?explanation ty_expected) in { c_lhs = pat; @@ -5059,7 +5194,7 @@ and type_cases raise (Error (loc, env, No_value_clauses)); let partial = if partial_flag then - check_partial ~lev env ty_arg_check loc val_cases + check_partial ~lev allow_modules env ty_arg_check loc val_cases else Partial in @@ -5068,8 +5203,8 @@ and type_cases check_absent_variant branch_env (as_comp_pattern category typed_pat) ) half_typed_cases; with_level_if delayed ~level:lev begin fun () -> - check_unused ~lev env ty_arg_check val_cases ; - check_unused ~lev env Predef.type_exn exn_cases ; + check_unused ~lev allow_modules env ty_arg_check val_cases ; + check_unused ~lev allow_modules env Predef.type_exn exn_cases ; end; Parmatch.check_ambiguous_bindings val_cases ; Parmatch.check_ambiguous_bindings exn_cases @@ -5087,34 +5222,20 @@ and type_cases (* Typing of let bindings *) and type_let ?check ?check_strict - existential_context env rec_flag spat_sexp_list allow = - let spatl = - List.map - (fun {pvb_pat=spat; pvb_expr=sexp; pvb_attributes=attrs} -> - attrs, - match spat.ppat_desc, sexp.pexp_desc with - (Ppat_any | Ppat_constraint _), _ -> spat - | _, Pexp_coerce (_, _, sty) - | _, Pexp_constraint (_, sty) when !Clflags.principal -> - (* propagate type annotation to pattern, - to allow it to be generalized in -principal mode *) - Ast_helper.Pat.constraint_ - ~loc:{spat.ppat_loc with Location.loc_ghost=true} - spat - sty - | _ -> spat) - spat_sexp_list in + existential_context env rec_flag spat_sexp_list allow_modules = + let spatl = List.map vb_pat_constraint spat_sexp_list in let attrs_list = List.map fst spatl in let is_recursive = (rec_flag = Recursive) in - let (pat_list, exp_list, new_env, unpacks, _pvs) = + let (pat_list, exp_list, new_env, mvs, _pvs) = with_local_level begin fun () -> if existential_context = At_toplevel then Typetexp.TyVarEnv.reset (); - let (pat_list, new_env, force, pvs, unpacks) = + let (pat_list, new_env, force, pvs, mvs) = with_local_level_if_principal begin fun () -> let nvs = List.map (fun _ -> newvar ()) spatl in - let (pat_list, _new_env, _force, _pvs, _unpacks as res) = - type_pattern_list Value existential_context env spatl nvs allow in + let (pat_list, _new_env, _force, _pvs, _mvs as res) = + type_pattern_list + Value existential_context env spatl nvs allow_modules in (* If recursive, first unify with an approximation of the expression *) if is_recursive then @@ -5144,6 +5265,17 @@ and type_let ?check ?check_strict List.iter (fun pat -> generalize_structure pat.pat_type) pat_list end in + (* Note [add_module_variables after checking expressions] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Don't call [add_module_variables] here, because its use of + [type_module] will fail until after we have type-checked the expression + of the let. Example: [let m : (module S) = ... in let (module M) = m in + ...] We learn the signature [S] from the type of [m] in the RHS of the + second let, and we need that knowledge for [type_module] to succeed. If + we type-checked expressions before patterns, then we could call + [add_module_variables] here. + *) + let new_env = add_pattern_variables new_env pvs in let pat_list = List.map (fun pat -> {pat with pat_type = instance pat.pat_type}) @@ -5153,10 +5285,18 @@ and type_let ?check ?check_strict List.iter (fun f -> f()) force; let exp_list = - let exp_env = if is_recursive then new_env else env in + (* See Note [add_module_variables after checking expressions] + We can't defer type-checking module variables with recursive + definitions, so things like [let rec (module M) = m in ...] always + fail, even if the type of [m] is known. + *) + let exp_env = + if is_recursive then add_module_variables new_env mvs else env + in type_let_def_wrap_warnings ?check ?check_strict ~is_recursive ~exp_env ~new_env ~spat_sexp_list ~attrs_list ~pat_list ~pvs - (fun exp_env {pvb_expr=sexp; pvb_attributes; _} pat -> + (fun exp_env ({pvb_attributes; _} as vb) pat -> + let sexp = vb_exp_constraint vb in match get_desc pat.pat_type with | Tpoly (ty, tl) -> let vars, ty' = @@ -5166,21 +5306,13 @@ and type_let ?check ?check_strict in let exp = Builtin_attributes.warning_scope pvb_attributes (fun () -> - if rec_flag = Recursive then - type_unpacks exp_env unpacks sexp (mk_expected ty') - else - type_expect exp_env sexp (mk_expected ty') - ) + type_expect exp_env sexp (mk_expected ty')) in exp, Some vars | _ -> let exp = Builtin_attributes.warning_scope pvb_attributes (fun () -> - if rec_flag = Recursive then - type_unpacks exp_env unpacks sexp - (mk_expected pat.pat_type) - else - type_expect exp_env sexp (mk_expected pat.pat_type)) + type_expect exp_env sexp (mk_expected pat.pat_type)) in exp, None) in @@ -5188,13 +5320,13 @@ and type_let ?check ?check_strict (fun pat (attrs, exp) -> Builtin_attributes.warning_scope ~ppwarning:false attrs (fun () -> - ignore(check_partial env pat.pat_type pat.pat_loc - [case pat exp]) + ignore(check_partial allow_modules env pat.pat_type pat.pat_loc + [case pat exp] : Typedtree.partial) ) ) pat_list (List.map2 (fun (attrs, _) (e, _) -> attrs, e) spatl exp_list); - (pat_list, exp_list, new_env, unpacks, + (pat_list, exp_list, new_env, mvs, List.map (fun pv -> { pv with pv_type = instance pv.pv_type}) pvs) end ~post: begin fun (pat_list, exp_list, _, _, pvs) -> @@ -5241,13 +5373,13 @@ and type_let ?check ?check_strict | Tpat_alias ({pat_desc=Tpat_any}, _, _) -> () | _ -> raise(Error(pat.pat_loc, env, Illegal_letrec_pat))) l; - List.iter (function - | {vb_pat = {pat_desc = Tpat_any; pat_extra; _}; vb_expr; _} -> - if not (List.exists (function (Tpat_constraint _, _, _) -> true - | _ -> false) pat_extra) then - check_partial_application ~statement:false vb_expr - | _ -> ()) l; - (l, new_env, unpacks) + List.iter (fun vb -> + if pattern_needs_partial_application_check vb.vb_pat then + check_partial_application ~statement:false vb.vb_expr + ) l; + (* See Note [add_module_variables after checking expressions] *) + let new_env = add_module_variables new_env mvs in + (l, new_env) and type_let_def_wrap_warnings ?(check = fun s -> Warnings.Unused_var s) @@ -5504,18 +5636,18 @@ and type_send env loc explanation e met = (* Typing of toplevel bindings *) let type_binding env rec_flag spat_sexp_list = - let (pat_exp_list, new_env, _unpacks) = + let (pat_exp_list, new_env) = type_let ~check:(fun s -> Warnings.Unused_value_declaration s) ~check_strict:(fun s -> Warnings.Unused_value_declaration s) At_toplevel - env rec_flag spat_sexp_list false + env rec_flag spat_sexp_list Modules_rejected in (pat_exp_list, new_env) let type_let existential_ctx env rec_flag spat_sexp_list = - let (pat_exp_list, new_env, _unpacks) = - type_let existential_ctx env rec_flag spat_sexp_list false in + let (pat_exp_list, new_env) = + type_let existential_ctx env rec_flag spat_sexp_list Modules_rejected in (pat_exp_list, new_env) (* Typing of toplevel expressions *) @@ -5655,7 +5787,7 @@ let report_this_function ppf funct = else Format.fprintf ppf "This function" let report_too_many_arg_error ~funct ~func_ty ~previous_arg_loc - ~extra_arg_loc loc = + ~extra_arg_loc ~returns_unit loc = let open Location in let cnum_offset off (pos : Lexing.position) = { pos with pos_cnum = pos.pos_cnum + off } @@ -5673,8 +5805,10 @@ let report_too_many_arg_error ~funct ~func_ty ~previous_arg_loc loc_end = cnum_offset ~+1 arg_end; loc_ghost = false } in - let sub = [ - msg ~loc:tail_loc "@{<hint>Hint@}: Did you forget a ';'?"; + let hint_semicolon = if returns_unit then [ + msg ~loc:tail_loc "@{<hint>Hint@}: Did you forget a ';'?"; + ] else [] in + let sub = hint_semicolon @ [ msg ~loc:extra_arg_loc "This extra argument is not expected."; ] in errorf ~loc:app_loc ~sub @@ -5732,11 +5866,17 @@ let report_error ~loc env = function fprintf ppf "This expression has type") (function ppf -> fprintf ppf "but an expression was expected of type"); - | Apply_non_function { funct; func_ty; previous_arg_loc; extra_arg_loc } -> + | Apply_non_function { + funct; func_ty; res_ty; previous_arg_loc; extra_arg_loc + } -> begin match get_desc func_ty with Tarrow _ -> + let returns_unit = match get_desc res_ty with + | Tconstr (p, _, _) -> Path.same p Predef.path_unit + | _ -> false + in report_too_many_arg_error ~funct ~func_ty ~previous_arg_loc - ~extra_arg_loc loc + ~extra_arg_loc ~returns_unit loc | _ -> Location.errorf ~loc "@[<v>@[<2>This expression has type@ %a@]@ %s@]" Printtyp.type_expr func_ty diff --git a/typing/typecore.mli b/typing/typecore.mli index 916aecdec3..6c73b08b4f 100644 --- a/typing/typecore.mli +++ b/typing/typecore.mli @@ -98,6 +98,10 @@ type existential_restriction = | In_class_def (** or in [class c = let ... in ...] *) | In_self_pattern (** or in self pattern *) +type module_patterns_restriction = + | Modules_allowed of { scope : int } + | Modules_rejected + val type_binding: Env.t -> rec_flag -> Parsetree.value_binding list -> @@ -117,7 +121,7 @@ val type_self_pattern: Env.t -> Parsetree.pattern -> Typedtree.pattern * pattern_variable list val check_partial: - ?lev:int -> Env.t -> type_expr -> + ?lev:int -> module_patterns_restriction -> Env.t -> type_expr -> Location.t -> Typedtree.value Typedtree.case list -> Typedtree.partial val type_expect: ?in_function:(Location.t * type_expr) -> @@ -158,6 +162,7 @@ type error = | Apply_non_function of { funct : Typedtree.expression; func_ty : type_expr; + res_ty : type_expr; previous_arg_loc : Location.t; extra_arg_loc : Location.t; } diff --git a/typing/typedtree.mli b/typing/typedtree.mli index 9ecf395602..985f7d8b25 100644 --- a/typing/typedtree.mli +++ b/typing/typedtree.mli @@ -69,6 +69,8 @@ and pat_extra = | Tpat_unpack (** (module P) { pat_desc = Tpat_var "P" ; pat_extra = (Tpat_unpack, _, _) :: ... } + (module _) { pat_desc = Tpat_any + ; pat_extra = (Tpat_unpack, _, _) :: ... } *) and 'k pattern_desc = diff --git a/typing/typemod.ml b/typing/typemod.ml index 1ba69f818d..b8badf2a99 100644 --- a/typing/typemod.ml +++ b/typing/typemod.ml @@ -57,8 +57,9 @@ type error = | With_changes_module_alias of Longident.t * Ident.t * Path.t | With_cannot_remove_constrained_type | Repeated_name of Sig_component_kind.t * string - | Non_generalizable of type_expr - | Non_generalizable_module of module_type + | Non_generalizable of { vars : type_expr list; expression : type_expr } + | Non_generalizable_module of + { vars : type_expr list; item : value_description; mty : module_type } | Implementation_is_required of string | Interface_not_compiled of string | Not_allowed_in_functor_body @@ -1829,11 +1830,11 @@ let path_of_module mexp = do not contain non-generalized type variable *) let rec nongen_modtype env = function - Mty_ident _ -> false - | Mty_alias _ -> false + Mty_ident _ -> None + | Mty_alias _ -> None | Mty_signature sg -> let env = Env.add_signature sg env in - List.exists (nongen_signature_item env) sg + List.find_map (nongen_signature_item env) sg | Mty_functor(arg_opt, body) -> let env = match arg_opt with @@ -1845,18 +1846,35 @@ let rec nongen_modtype env = function nongen_modtype env body and nongen_signature_item env = function - Sig_value(_id, desc, _) -> Ctype.nongen_schema env desc.val_type + | Sig_value(_id, desc, _) -> + Ctype.nongen_vars_in_schema env desc.val_type + |> Option.map (fun vars -> (vars, desc)) | Sig_module(_id, _, md, _, _) -> nongen_modtype env md.md_type - | _ -> false + | _ -> None + +let check_nongen_modtype env loc mty = + nongen_modtype env mty + |> Option.iter (fun (vars, item) -> + let vars = Btype.TypeSet.elements vars in + let error = + Non_generalizable_module { vars; item; mty } + in + raise(Error(loc, env, error)) + ) let check_nongen_signature_item env sig_item = match sig_item with Sig_value(_id, vd, _) -> - if Ctype.nongen_schema env vd.val_type then - raise (Error (vd.val_loc, env, Non_generalizable vd.val_type)) + Ctype.nongen_vars_in_schema env vd.val_type + |> Option.iter (fun vars -> + let vars = Btype.TypeSet.elements vars in + let error = + Non_generalizable { vars; expression = vd.val_type } + in + raise (Error (vd.val_loc, env, error)) + ) | Sig_module (_id, _, md, _, _) -> - if nongen_modtype env md.md_type then - raise(Error(md.md_loc, env, Non_generalizable_module md.md_type)) + check_nongen_modtype env md.md_loc md.md_type | _ -> () let check_nongen_signature env sg = @@ -2877,8 +2895,7 @@ let type_module_type_of env smod = in let mty = Mtype.scrape_for_type_of ~remove_aliases env tmty.mod_type in (* PR#5036: must not contain non-generalized type variables *) - if nongen_modtype env mty then - raise(Error(smod.pmod_loc, env, Non_generalizable_module mty)); + check_nongen_modtype env smod.pmod_loc mty; tmty, mty (* For Typecore *) @@ -3281,14 +3298,36 @@ let report_error ~loc _env = function "@[Multiple definition of the %s name %s.@ \ Names must be unique in a given structure or signature.@]" (Sig_component_kind.to_string kind) name - | Non_generalizable typ -> + | Non_generalizable { vars; expression } -> + let[@manual.ref "ss:valuerestriction"] manual_ref = [ 6; 1; 2 ] in + prepare_for_printing vars; + add_type_to_preparation expression; Location.errorf ~loc "@[The type of this expression,@ %a,@ \ - contains type variables that cannot be generalized@]" type_scheme typ - | Non_generalizable_module mty -> - Location.errorf ~loc + contains the non-generalizable type variable(s): %a.@ %a@]" + prepared_type_scheme expression + (pp_print_list ~pp_sep:(fun f () -> fprintf f ",@ ") + prepared_type_scheme) vars + Misc.print_see_manual manual_ref + | Non_generalizable_module { vars; mty; item } -> + let[@manual.ref "ss:valuerestriction"] manual_ref = [ 6; 1; 2 ] in + prepare_for_printing vars; + add_type_to_preparation item.val_type; + let sub = + [ Location.msg ~loc:item.val_loc + "The type of this value,@ %a,@ \ + contains the non-generalizable type variable(s) %a." + prepared_type_scheme + item.val_type + (pp_print_list ~pp_sep:(fun f () -> fprintf f ",@ ") + prepared_type_scheme) vars + ] + in + Location.errorf ~loc ~sub "@[The type of this module,@ %a,@ \ - contains type variables that cannot be generalized@]" modtype mty + contains non-generalizable type variable(s).@ %a@]" + modtype mty + Misc.print_see_manual manual_ref | Implementation_is_required intf_name -> Location.errorf ~loc "@[The interface %a@ declares values, not just types.@ \ diff --git a/typing/typemod.mli b/typing/typemod.mli index f0073a8c4c..859c2e9b3d 100644 --- a/typing/typemod.mli +++ b/typing/typemod.mli @@ -116,8 +116,9 @@ type error = | With_changes_module_alias of Longident.t * Ident.t * Path.t | With_cannot_remove_constrained_type | Repeated_name of Sig_component_kind.t * string - | Non_generalizable of type_expr - | Non_generalizable_module of module_type + | Non_generalizable of { vars : type_expr list; expression : type_expr } + | Non_generalizable_module of + { vars : type_expr list; item : value_description; mty : module_type } | Implementation_is_required of string | Interface_not_compiled of string | Not_allowed_in_functor_body diff --git a/typing/typetexp.ml b/typing/typetexp.ml index 3b7b06e52f..ab20777e15 100644 --- a/typing/typetexp.ml +++ b/typing/typetexp.ml @@ -33,7 +33,6 @@ type error = | Type_arity_mismatch of Longident.t * int * int | Bound_type_variable of string | Recursive_type - | Unbound_row_variable of Longident.t | Type_mismatch of Errortrace.unification_error | Alias_type_mismatch of Errortrace.unification_error | Present_has_conjunction of string @@ -152,7 +151,6 @@ end = struct let reset () = reset_global_level (); - Ctype.reset_reified_var_counter (); type_variables := TyVarMap.empty let is_in_scope name = @@ -838,10 +836,6 @@ let report_error env ppf = function fprintf ppf "Already bound type parameter %a" Pprintast.tyvar name | Recursive_type -> fprintf ppf "This type is recursive" - | Unbound_row_variable lid -> - (* we don't use "spellcheck" here: this error is not raised - anywhere so it's unclear how it should be handled *) - fprintf ppf "Unbound row variable in #%a" longident lid | Type_mismatch trace -> Printtyp.report_unification_error ppf Env.empty trace (function ppf -> diff --git a/typing/typetexp.mli b/typing/typetexp.mli index ca058a5cf0..fb2eda068f 100644 --- a/typing/typetexp.mli +++ b/typing/typetexp.mli @@ -73,7 +73,6 @@ type error = | Type_arity_mismatch of Longident.t * int * int | Bound_type_variable of string | Recursive_type - | Unbound_row_variable of Longident.t | Type_mismatch of Errortrace.unification_error | Alias_type_mismatch of Errortrace.unification_error | Present_has_conjunction of string |