mirror of
https://github.com/ocaml/ocaml.git
synced 2026-08-01 06:50:29 +08:00
Constants don't have a location in interval patterns. This adds the location information on every constants. Co-authored-by: Guillaume Petiot <guillaume@tarides.com>
384 lines
12 KiB
OCaml
384 lines
12 KiB
OCaml
(**************************************************************************)
|
|
(* *)
|
|
(* OCaml *)
|
|
(* *)
|
|
(* Pierre Chambart, OCamlPro *)
|
|
(* *)
|
|
(* Copyright 2015 Institut National de Recherche en Informatique et *)
|
|
(* en Automatique. *)
|
|
(* *)
|
|
(* All rights reserved. This file is distributed under the terms of *)
|
|
(* the GNU Lesser General Public License version 2.1, with the *)
|
|
(* special exception on linking described in the file LICENSE. *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
open Typedtree
|
|
open Lambda
|
|
open Location
|
|
open Builtin_attributes
|
|
|
|
let return_if_flambda =
|
|
if Config.flambda then Return else Mark_used_only
|
|
|
|
let is_inline_attribute =
|
|
[ "inline", Return ]
|
|
|
|
let is_inlined_attribute =
|
|
[ "inlined", Return
|
|
; "unrolled", return_if_flambda
|
|
]
|
|
|
|
let is_specialise_attribute =
|
|
[ "specialise", return_if_flambda ]
|
|
|
|
let is_specialised_attribute =
|
|
[ "specialised", return_if_flambda ]
|
|
|
|
let is_local_attribute =
|
|
[ "local", Return ]
|
|
|
|
let is_tailcall_attribute =
|
|
[ "tailcall", Return ]
|
|
|
|
let is_tmc_attribute =
|
|
[ "tail_mod_cons", Return ]
|
|
|
|
let is_poll_attribute =
|
|
[ "poll", Return ]
|
|
|
|
let find_attribute p attributes =
|
|
let inline_attribute = select_attributes p attributes in
|
|
let attr =
|
|
match inline_attribute with
|
|
| [] -> None
|
|
| [attr] -> Some attr
|
|
| attr :: {Parsetree.attr_name = {txt;loc}; _} :: _ ->
|
|
Location.prerr_warning loc (Warnings.Duplicated_attribute txt);
|
|
Some attr
|
|
in
|
|
attr
|
|
|
|
let get_payload get_from_exp =
|
|
let open Parsetree in
|
|
function
|
|
| PStr [{pstr_desc = Pstr_eval (exp, [])}] -> get_from_exp exp
|
|
| _ -> Result.Error ()
|
|
|
|
let get_optional_payload get_from_exp =
|
|
let open Parsetree in
|
|
function
|
|
| PStr [] -> Result.Ok None
|
|
| other -> Result.map Option.some (get_payload get_from_exp other)
|
|
|
|
let get_id_from_exp =
|
|
let open Parsetree in
|
|
function
|
|
| { pexp_desc = Pexp_ident { txt = Longident.Lident id } } -> Result.Ok id
|
|
| _ -> Result.Error ()
|
|
|
|
let get_int_from_exp =
|
|
let open Parsetree in
|
|
function
|
|
| { pexp_desc = Pexp_constant
|
|
{pconst_desc = Pconst_integer(s, None); _} } ->
|
|
begin match Misc.Int_literal_converter.int s with
|
|
| n -> Result.Ok n
|
|
| exception (Failure _) -> Result.Error ()
|
|
end
|
|
| _ -> Result.Error ()
|
|
|
|
let get_construct_from_exp =
|
|
let open Parsetree in
|
|
function
|
|
| { pexp_desc =
|
|
Pexp_construct ({ txt = Longident.Lident constr }, None) } ->
|
|
Result.Ok constr
|
|
| _ -> Result.Error ()
|
|
|
|
let get_bool_from_exp exp =
|
|
Result.bind (get_construct_from_exp exp)
|
|
(function
|
|
| "true" -> Result.Ok true
|
|
| "false" -> Result.Ok false
|
|
| _ -> Result.Error ())
|
|
|
|
let parse_id_payload txt loc ~default ~empty cases payload =
|
|
let[@local] warn () =
|
|
let ( %> ) f g x = g (f x) in
|
|
let msg =
|
|
cases
|
|
|> List.map (fst %> Printf.sprintf "'%s'")
|
|
|> String.concat ", "
|
|
|> Printf.sprintf "It must be either %s or empty"
|
|
in
|
|
Location.prerr_warning loc (Warnings.Attribute_payload (txt, msg));
|
|
default
|
|
in
|
|
match get_optional_payload get_id_from_exp payload with
|
|
| Error () -> warn ()
|
|
| Ok None -> empty
|
|
| Ok (Some id) ->
|
|
match List.assoc_opt id cases with
|
|
| Some r -> r
|
|
| None -> warn ()
|
|
|
|
let parse_inline_attribute attr =
|
|
match attr with
|
|
| None -> Default_inline
|
|
| Some ({Parsetree.attr_name = {txt;loc}; attr_payload = payload} as attr) ->
|
|
if attr_equals_builtin attr "unrolled" then begin
|
|
(* the 'unrolled' attributes must be used as [@unrolled n]. *)
|
|
let warning txt = Warnings.Attribute_payload
|
|
(txt, "It must be an integer literal")
|
|
in
|
|
match get_payload get_int_from_exp payload with
|
|
| Ok n -> Unroll n
|
|
| Error () ->
|
|
Location.prerr_warning loc (warning txt);
|
|
Default_inline
|
|
end else
|
|
parse_id_payload txt loc
|
|
~default:Default_inline
|
|
~empty:Always_inline
|
|
[
|
|
"never", Never_inline;
|
|
"always", Always_inline;
|
|
"hint", Hint_inline;
|
|
]
|
|
payload
|
|
|
|
let parse_specialise_attribute attr =
|
|
match attr with
|
|
| None -> Default_specialise
|
|
| Some {Parsetree.attr_name = {txt; loc}; attr_payload = payload} ->
|
|
parse_id_payload txt loc
|
|
~default:Default_specialise
|
|
~empty:Always_specialise
|
|
[
|
|
"never", Never_specialise;
|
|
"always", Always_specialise;
|
|
]
|
|
payload
|
|
|
|
let parse_local_attribute attr =
|
|
match attr with
|
|
| None -> Default_local
|
|
| Some {Parsetree.attr_name = {txt; loc}; attr_payload = payload} ->
|
|
parse_id_payload txt loc
|
|
~default:Default_local
|
|
~empty:Always_local
|
|
[
|
|
"never", Never_local;
|
|
"always", Always_local;
|
|
"maybe", Default_local;
|
|
]
|
|
payload
|
|
|
|
let parse_poll_attribute attr =
|
|
match attr with
|
|
| None -> Default_poll
|
|
| Some {Parsetree.attr_name = {txt; loc}; attr_payload = payload} ->
|
|
parse_id_payload txt loc
|
|
~default:Default_poll
|
|
~empty:Default_poll
|
|
[
|
|
"error", Error_poll;
|
|
]
|
|
payload
|
|
|
|
let get_inline_attribute l =
|
|
let attr = find_attribute is_inline_attribute l in
|
|
parse_inline_attribute attr
|
|
|
|
let get_specialise_attribute l =
|
|
let attr = find_attribute is_specialise_attribute l in
|
|
parse_specialise_attribute attr
|
|
|
|
let get_local_attribute l =
|
|
let attr = find_attribute is_local_attribute l in
|
|
parse_local_attribute attr
|
|
|
|
let get_poll_attribute l =
|
|
let attr = find_attribute is_poll_attribute l in
|
|
parse_poll_attribute attr
|
|
|
|
let check_local_inline loc attr =
|
|
match attr.local, attr.inline with
|
|
| Always_local, (Always_inline | Hint_inline | Unroll _) ->
|
|
Location.prerr_warning loc
|
|
(Warnings.Duplicated_attribute "local/inline")
|
|
| _ ->
|
|
()
|
|
|
|
let check_poll_inline loc attr =
|
|
match attr.poll, attr.inline with
|
|
| Error_poll, (Always_inline | Hint_inline | Unroll _) ->
|
|
Location.prerr_warning loc
|
|
(Warnings.Inlining_impossible
|
|
"[@poll error] is incompatible with inlining")
|
|
| _ ->
|
|
()
|
|
|
|
let check_poll_local loc attr =
|
|
match attr.poll, attr.local with
|
|
| Error_poll, Always_local ->
|
|
Location.prerr_warning loc
|
|
(Warnings.Inlining_impossible
|
|
"[@poll error] is incompatible with local function optimization")
|
|
| _ ->
|
|
()
|
|
|
|
let lfunction_with_attr ~attr { kind; params; return; body; attr=_; loc } =
|
|
lfunction ~kind ~params ~return ~body ~attr ~loc
|
|
|
|
let add_inline_attribute expr loc attributes =
|
|
match expr with
|
|
| Lfunction({ attr = { stub = false } as attr } as funct) ->
|
|
begin match get_inline_attribute attributes with
|
|
| Default_inline -> expr
|
|
| (Always_inline | Hint_inline | Never_inline | Unroll _)
|
|
as inline ->
|
|
begin match attr.inline with
|
|
| Default_inline -> ()
|
|
| Always_inline | Hint_inline | Never_inline | Unroll _ ->
|
|
Location.prerr_warning loc
|
|
(Warnings.Duplicated_attribute "inline")
|
|
end;
|
|
let attr = { attr with inline } in
|
|
check_local_inline loc attr;
|
|
check_poll_inline loc attr;
|
|
lfunction_with_attr ~attr funct
|
|
end
|
|
| _ -> expr
|
|
|
|
let add_specialise_attribute expr loc attributes =
|
|
match expr with
|
|
| Lfunction({ attr = { stub = false } as attr } as funct) ->
|
|
begin match get_specialise_attribute attributes with
|
|
| Default_specialise -> expr
|
|
| (Always_specialise | Never_specialise) as specialise ->
|
|
begin match attr.specialise with
|
|
| Default_specialise -> ()
|
|
| Always_specialise | Never_specialise ->
|
|
Location.prerr_warning loc
|
|
(Warnings.Duplicated_attribute "specialise")
|
|
end;
|
|
let attr = { attr with specialise } in
|
|
lfunction_with_attr ~attr funct
|
|
end
|
|
| _ -> expr
|
|
|
|
let add_local_attribute expr loc attributes =
|
|
match expr with
|
|
| Lfunction({ attr = { stub = false } as attr } as funct) ->
|
|
begin match get_local_attribute attributes with
|
|
| Default_local -> expr
|
|
| (Always_local | Never_local) as local ->
|
|
begin match attr.local with
|
|
| Default_local -> ()
|
|
| Always_local | Never_local ->
|
|
Location.prerr_warning loc
|
|
(Warnings.Duplicated_attribute "local")
|
|
end;
|
|
let attr = { attr with local } in
|
|
check_local_inline loc attr;
|
|
check_poll_local loc attr;
|
|
lfunction_with_attr ~attr funct
|
|
end
|
|
| _ -> expr
|
|
|
|
let add_tmc_attribute expr loc attributes =
|
|
match expr with
|
|
| Lfunction funct ->
|
|
let attr = find_attribute is_tmc_attribute attributes in
|
|
begin match attr with
|
|
| None -> expr
|
|
| Some _ ->
|
|
if funct.attr.tmc_candidate then
|
|
Location.prerr_warning loc
|
|
(Warnings.Duplicated_attribute "tail_mod_cons");
|
|
let attr = { funct.attr with tmc_candidate = true } in
|
|
lfunction_with_attr ~attr funct
|
|
end
|
|
| _ -> expr
|
|
|
|
let add_poll_attribute expr loc attributes =
|
|
match expr with
|
|
| Lfunction({ attr = { stub = false } as attr } as funct) ->
|
|
begin match get_poll_attribute attributes with
|
|
| Default_poll -> expr
|
|
| Error_poll as poll ->
|
|
begin match attr.poll with
|
|
| Default_poll -> ()
|
|
| Error_poll ->
|
|
Location.prerr_warning loc
|
|
(Warnings.Duplicated_attribute "poll error")
|
|
end;
|
|
let attr = { attr with poll } in
|
|
check_poll_inline loc attr;
|
|
check_poll_local loc attr;
|
|
let attr = { attr with inline = Never_inline; local = Never_local } in
|
|
lfunction_with_attr ~attr funct
|
|
end
|
|
| expr -> expr
|
|
|
|
(* Get the [@inlined] attribute payload (or default if not present). *)
|
|
let get_inlined_attribute e =
|
|
let attr = find_attribute is_inlined_attribute e.exp_attributes in
|
|
parse_inline_attribute attr
|
|
|
|
let get_inlined_attribute_on_module e =
|
|
let rec get mod_expr =
|
|
let attr = find_attribute is_inlined_attribute mod_expr.mod_attributes in
|
|
let attr = parse_inline_attribute attr in
|
|
let attr =
|
|
match mod_expr.Typedtree.mod_desc with
|
|
| Tmod_constraint (me, _, _, _) ->
|
|
let inner_attr = get me in
|
|
begin match attr with
|
|
| Always_inline | Hint_inline | Never_inline | Unroll _ -> attr
|
|
| Default_inline -> inner_attr
|
|
end
|
|
| _ -> attr
|
|
in
|
|
attr
|
|
in
|
|
get e
|
|
|
|
let get_specialised_attribute e =
|
|
let attr = find_attribute is_specialised_attribute e.exp_attributes in
|
|
parse_specialise_attribute attr
|
|
|
|
let get_tailcall_attribute e =
|
|
let attr = find_attribute is_tailcall_attribute e.exp_attributes in
|
|
match attr with
|
|
| None -> Default_tailcall
|
|
| Some {Parsetree.attr_name = {txt; loc}; attr_payload = payload} ->
|
|
match get_optional_payload get_bool_from_exp payload with
|
|
| Ok (None | Some true) -> Tailcall_expectation true
|
|
| Ok (Some false) -> Tailcall_expectation false
|
|
| Error () ->
|
|
let msg = "Only an optional boolean literal is supported." in
|
|
Location.prerr_warning loc (Warnings.Attribute_payload (txt, msg));
|
|
Default_tailcall
|
|
|
|
let add_function_attributes lam loc attr =
|
|
let lam =
|
|
add_inline_attribute lam loc attr
|
|
in
|
|
let lam =
|
|
add_specialise_attribute lam loc attr
|
|
in
|
|
let lam =
|
|
add_local_attribute lam loc attr
|
|
in
|
|
let lam =
|
|
add_tmc_attribute lam loc attr
|
|
in
|
|
let lam =
|
|
(* last because poll overrides inline and local *)
|
|
add_poll_attribute lam loc attr
|
|
in
|
|
lam
|