From 0ad8b47ce2ec578eab64896a3ba8af3f71ce2fd1 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 5 Nov 2015 17:09:09 +0100 Subject: stdlib: Pretty-print constraints as 'V :: T' Print constraints as 'V :: T' rather than 'is_subtype(V, T)'. --- lib/stdlib/src/erl_pp.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index f7a969977a..9c812395e3 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -253,6 +253,8 @@ lattribute(import, Name, _Opts, _State) when is_list(Name) -> attr("import", [{var,a0(),pname(Name)}]); lattribute(import, {From,Falist}, _Opts, _State) -> attr("import",[{var,a0(),pname(From)},falist(Falist)]); +lattribute(export_type, Talist, _Opts, _State) -> + call({var,a0(),"-export_type"}, [falist(Talist)], 0, options(none)); lattribute(optional_callbacks, Falist, Opts, _State) -> ArgL = try falist(Falist) catch _:_ -> abstract(Falist, Opts) @@ -321,7 +323,6 @@ ltype({type,_,'fun',[{type,_,any},_]}=FunType, _) -> ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType, _) -> [fun_type(['fun',$(], FunType),$)]; ltype({type,Line,T,Ts}, _) -> - %% Compatibility. Before 18.0. simple_type({atom,Line,T}, Ts); ltype({user_type,Line,T,Ts}, _) -> simple_type({atom,Line,T}, Ts); @@ -399,6 +400,9 @@ guard_type(Before, Gs) -> Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, Opts)}]}, {list,[{step,Before,Gl}]}. +constraint({type,_Line,constraint,[{atom,_,is_subtype},[{var,_,_}=V,Type]]}, + _Opts) -> + typed(lexpr(V, options(none)), Type); constraint({type,_Line,constraint,[Tag,As]}, _Opts) -> simple_type(Tag, As). -- cgit v1.2.3 From b8a5a201389c117aaa8ddfa387dcb11fb6f5ae4a Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 17 Nov 2015 14:09:59 +0100 Subject: stdlib: Correct pretty-printing of map types Add parentheses around annotated type union elements in map pair types. The bug was introduced in Erlang/OTP 18.0. --- lib/stdlib/src/erl_pp.erl | 15 ++------------- lib/stdlib/test/erl_pp_SUITE.erl | 3 +++ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 9c812395e3..c5177aca90 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -347,16 +347,8 @@ map_type(Fs) -> map_pair_types(Fs) -> tuple_type(Fs, fun map_pair_type/2). -map_pair_type({type,_Line,map_field_assoc,[Ktype,Vtype]}, Prec) -> - map_assoc_typed(ltype(Ktype), Vtype, Prec). - -map_assoc_typed(B, {type,_,union,Ts}, Prec) -> - {first,[B,$\s],{seq,[],[],[],map_assoc_union_type(Ts, Prec)}}; -map_assoc_typed(B, Type, Prec) -> - {list,[{cstep,[B," =>"],ltype(Type, Prec)}]}. - -map_assoc_union_type([T|Ts], Prec) -> - [[leaf("=> "),ltype(T)] | ltypes(Ts, fun union_elem/2, Prec)]. +map_pair_type({type,_Line,map_field_assoc,[KType,VType]}, Prec) -> + {list,[{cstep,[ltype(KType, Prec),leaf(" =>")],ltype(VType, Prec)}]}. record_type(Name, Fields) -> {first,[record_name(Name)],field_types(Fields)}. @@ -371,9 +363,6 @@ typed(B, Type) -> {_L,_P,R} = type_inop_prec('::'), {list,[{cstep,[B,' ::'],ltype(Type, R)}]}. -union_elem(T, Prec) -> - [leaf(" | "),ltype(T, Prec)]. - tuple_type(Ts, F) -> {seq,${,$},[$,],ltypes(Ts, F, 0)}. diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 389fd059f6..92e2764c65 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -960,6 +960,9 @@ maps_syntax(Config) when is_list(Config) -> "-compile(export_all).\n" "-type t1() :: map().\n" "-type t2() :: #{ atom() => integer(), atom() => float() }.\n" + "-type u() :: #{a => (I :: integer()) | (A :: atom()),\n" + " (X :: atom()) | (Y :: atom()) =>\n" + " (I :: integer()) | (A :: atom())}.\n" "-spec f1(t1()) -> 'true'.\n" "f1(M) when is_map(M) -> true.\n" "-spec f2(t2()) -> integer().\n" -- cgit v1.2.3 From 23885a8ab609688641098a759110e295052323f8 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 1 Dec 2015 10:58:43 +0100 Subject: erts: Correct the types section in The Abstract Format document The Types section is more consistent with Kostis' text in The Reference Manual. --- erts/doc/src/absform.xml | 375 ++++++++++++++++++++++------------------------- 1 file changed, 178 insertions(+), 197 deletions(-) diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index df2553ced3..8584898a9d 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -61,7 +61,7 @@

- Module declarations and forms + Module Declarations and Forms

A module declaration consists of a sequence of forms that are either function declarations or attributes.

@@ -78,204 +78,87 @@ Rep(F) = . If F is an attribute , then Rep(F) = . + If F is an attribute , then + Rep(F) = . If F is an attribute , then Rep(F) = . If F is an attribute , then Rep(F) = . - If F is a record declaration , then - Rep(F) = - . For Rep(V), see below. - If F is a type attribute (i.e. or - ) - where each - is a variable, then Rep(F) = - . - For Rep(T), see below. - If F is a type spec (i.e. or - ) - , - where each is a fun type clause with an - argument sequence of the same length , then - Rep(F) = - . - For Rep(Tc_i), see below. - If F is a type spec (i.e. or - ) - , - where each is a fun type clause with an - argument sequence of the same length , then - Rep(F) = - . - For Rep(Tc_i), see below. + If F is a record declaration + -record(Name,{V_1, ..., V_k}), then Rep(F) = + {attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}. + For Rep(V), see below. + If F is a type declaration + -Type Name(V_1, ..., V_k) :: T, where + Type is either the atom type or the atom opaque, + each V_i is a variable, and T is a type, then Rep(F) = + {attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}. + + If F is a function specification + -Spec Name Ft_1; ...; Ft_k, + where Spec is either the atom spec or the atom + callback, and each Ft_i is a possibly constrained + function type with an argument sequence of the same length + Arity, then Rep(F) = + {attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}. + + If F is a function specification + -spec Mod:Name Ft_1; ...; Ft_k, + where each Ft_i is a possibly constrained + function type with an argument sequence of the same length + Arity, then Rep(F) = + {attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}. + If F is a wild attribute , then Rep(F) = .

- If F is a function declaration , - where each is a function clause with a - pattern sequence of the same length , then - Rep(F) = . + If F is a function declaration + Name Fc_1 ; ... ; Name Fc_k, + where each Fc_i is a function clause with a + pattern sequence of the same length Arity, then + Rep(F) = {function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}. +
- Type clauses - - If T is a fun type clause - Ret]]>, where each - and are types, then - Rep(T) = - . - - If T is a bounded fun type clause , - where is an unbounded fun type clause and - is a type guard sequence, then Rep(T) = - . - -
- -
- Type guards - - If G is a constraint , where - is an atom and each is a - type, then Rep(G) = - . - - If G is a type definition , - where is a variable and - is a type, then Rep(G) = - . - -
- -
- Types - - If T is a type definition , - where is a variable and - is a type, then Rep(T) = - . - If T is a type union , - where each is a type, then Rep(T) = - . - If T is a type range , - where and are types, then - Rep(T) = . - If T is a binary operation , - where is an arithmetic or bitwise binary operator - and and are types, then - Rep(T) = . - If T is , where is an - arithmetic or bitwise unary operator and is a - type, then Rep(T) = . - If T is a fun type , then Rep(T) = - . - If T is a variable , then Rep(T) = - , where is an atom - with a printname consisting of the same characters as - . - If T is an atomic literal L and L is not a string literal, then - Rep(T) = Rep(L). - If T is a tuple or map type (i.e. - or ), then Rep(T) = - . - If T is a type , where each - is a type, then Rep(T) = - . - If T is a remote type , where - each is a type and and - , then Rep(T) = - . - - If T is the nil type , then Rep(T) = - . - If T is a list type , where - is a type, then Rep(T) = - . - If T is a non-empty list type , where - is a type, then Rep(T) = - . - If T is a map type , where each - is a map pair type, then Rep(T) = - . - If T is a map pair type V]]>, where - and are types, - then Rep(T) = - . - If T is a tuple type , where - each is a type, then Rep(T) = - . - If T is a record type , where - is an atom, then Rep(T) = - . - If T is a record type , - where is an atom, then Rep(T) = - . - - If T is a record field type , - where is an atom, then Rep(T) = - . - If T is a record field type >]]>, then Rep(T) = - . - - If T is a binary type >]]>, where - is a type, then Rep(T) = - . - If T is a binary type >]]>, - where is a type, then Rep(T) = - . - If T is a binary type >]]>, - where and is a type, then - Rep(T) = - . - - If T is a fun type Ret)]]>, then - Rep(T) = . - - If T is a fun type , where - is an unbounded fun type clause, - then Rep(T) = . - -
- -
- Record fields + Record Fields

Each field in a record declaration may have an optional - explicit default initializer expression

+ explicit default initializer expression, as well as an + optional type.

If V is , then Rep(V) = . - If V is , then + If V is , + where E is an expression, then Rep(V) = . - If V is , where is - an atom and is a type and it does not contain - syntactically, then Rep(V) = - . - Note that if is an annotated type, it will be wrapped in - parentheses. - If V is , where is - an atom and is a type, then Rep(V) = - . - - If V is , where - is an atom, is an expression and - is a type, then Rep(V) = - . - + If V is A :: T, where T is a + type and it does not contain + undefined syntactically, then Rep(V) = + {typed_record_field,{record_field,LINE,Rep(A)},Rep(undefined | T)}. + + If V is A :: T, where T is a type, then Rep(V) = + {typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}. + + If V is A = E :: T, where + E is an expression and T is a type, then Rep(V) = + {typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}. +
- Representation of parse errors and end of file + Representation of Parse Errors and End-of-file

In addition to the representations of forms, the list that represents - a module declaration (as returned by functions in and - ) may contain tuples and , denoting - syntactically incorrect forms and warnings, and , denoting an end - of stream encountered before a complete form had been parsed.

+ a module declaration (as returned by functions in erl_parse and + epp) may contain tuples {error,E} and + {warning,W}, denoting syntactically incorrect forms and + warnings, and {eof,LINE}, denoting an end-of-stream + encountered before a complete form had been parsed.

- Atomic literals + Atomic Literals

There are five kinds of atomic literals, which are represented in the same way in patterns, expressions and guards:

@@ -330,12 +213,12 @@ time), then Rep(P) = . If P is a record pattern , then Rep(P) = - . + . If P is , then Rep(P) = . If P is , then Rep(P) = , - i.e., patterns cannot be distinguished from their bodies. + that is, patterns cannot be distinguished from their bodies.

Note that every pattern has the same source form as some expression, and is represented the same way as the corresponding expression.

@@ -372,10 +255,10 @@ Rep(E) = . If E is , then Rep(E) = - . + . If E is , then Rep(E) = - . + . If E is , then Rep(E) = . If E is , then @@ -466,20 +349,13 @@ is a function clause then Rep(E) = . - If E is , - where each is a generator or a filter, then - Rep(E) = . - For Rep(W), see below. - If E is , a Mnesia record access - inside a query, then - Rep(E) = . If E is , then - Rep(E) = , - i.e., parenthesized expressions cannot be distinguished from their bodies. + Rep(E) = Rep(E_0), that is, parenthesized + expressions cannot be distinguished from their bodies.
- Generators and filters + Generators and Filters

When W is a generator or a filter (in the body of a list or binary comprehension), then:

If W is a generator , where is a pattern and @@ -494,20 +370,20 @@
- Binary element type specifiers + Binary Element Type Specifiers

A type specifier list TSL for a binary element is a sequence of type specifiers . Rep(TSL) = .

When TS is a type specifier for a binary element, then:

- If TS is an atom , Rep(TS) = . + If TS is an atom , then Rep(TS) = . If TS is a couple where is an atom and - is an integer, Rep(TS) = . + is an integer, then Rep(TS) = {A,Value}.
- Map assoc and exact fields + Map Assoc and Exact Fields

When W is an assoc or exact field (in the body of a map), then:

If W is an assoc field V]]>, where @@ -595,7 +471,7 @@ Rep(Gt) = . If Gt is , then Rep(E) = - . + . If Gt is , then Rep(Gt) = . If Gt is , then @@ -609,15 +485,120 @@ the atom and is an atom or an operator, then Rep(Gt) = . If Gt is , then - Rep(Gt) = , - i.e., parenthesized guard tests cannot be distinguished from their bodies. + Rep(Gt) = , that is, parenthesized + guard tests cannot be distinguished from their bodies.

Note that every guard test has the same source form as some expression, and is represented the same way as the corresponding expression.

- The abstract format after preprocessing + Types + + If T is an annotated type Anno :: Type, + where Anno is a variable and + Type is a type, then Rep(T) = + {ann_type,LINE,[Rep(Anno),Rep(Type)]}. + If T is an atom or integer literal L, then Rep(T) = Rep(L). + + If T is L Op R, + where Op is a binary operator and L and R + are types (this is an occurrence of an expression that can be + evaluated to an integer at compile time), then + Rep(T) = {op,LINE,Op,Rep(L),Rep(R)}. + If T is Op A, where Op is a + unary operator and A is a type (this is an occurrence of + an expression that can be evaluated to an integer at compile time), + then Rep(T) = {op,LINE,Op,Rep(A)}. + If T is a bitstring type <<_:M,_:_*N>>, + where M and N are singleton integer types, then Rep(T) = + {type,LINE,binary,[Rep(M),Rep(N)]}. + If T is the empty list type [], then Rep(T) = + {type,Line,nil,[]}. + If T is a fun type fun(), then Rep(T) = + {type,LINE,'fun',[]}. + If T is a fun type fun((...) -> B), + where B is a type, then + Rep(T) = {type,LINE,'fun',[{type,LINE,any},Rep(B)]}. + + If T is a fun type fun(Ft), where + Ft is a function type, + then Rep(T) = Rep(Ft). + If T is an integer range type L .. H, + where L and H are singleton integer types, then + Rep(T) = {type,LINE,range,[Rep(L),Rep(H)]}. + If T is a map type map(), then Rep(T) = + {type,LINE,map,any}. + If T is a map type #{P_1, ..., P_k}, where each + P_i is a map pair type, then Rep(T) = + {type,LINE,map,[Rep(P_1), ..., Rep(P_k)]}. + If T is a map pair type K => V, where + K and V are types, then Rep(T) = + {type,LINE,map_field_assoc,[Rep(K),Rep(V)]}. + If T is a predefined (or built-in) type N(A_1, ..., A_k), + where each A_i is a type, then Rep(T) = + {type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}. + If T is a record type #Name{F_1, ..., F_k}, + where each F_i is a record field type, then Rep(T) = + {type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}. + + If T is a record field type Name :: Type, + where Type is a type, then Rep(T) = + {type,LINE,field_type,[Rep(Name),Rep(Type)]}. + If T is a remote type M:N(A_1, ..., A_k), where + each A_i is a type, then Rep(T) = + {remote_type,LINE,[Rep(M),Rep(N),[Rep(A_1), ..., Rep(A_k)]]}. + + If T is a tuple type tuple(), then Rep(T) = + {type,LINE,tuple,any}. + If T is a tuple type {A_1, ..., A_k}, where + each A_i is a type, then Rep(T) = + {type,LINE,tuple,[Rep(A_1), ..., Rep(A_k)]}. + If T is a type union T_1 | ... | T_k, + where each T_i is a type, then Rep(T) = + {type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}. + If T is a type variable V, then Rep(T) = + {var,LINE,A}, where A is an atom with a printname + consisting of the same characters as V. A type variable + is any variable except underscore (_). + If T is a user-defined type N(A_1, ..., A_k), + where each A_i is a type, then Rep(T) = + {user_type,LINE,N,[Rep(A_1), ..., Rep(A_k)]}. + If T is ( T_0 ), then Rep(T) = Rep(T_0), + that is, parenthesized types cannot be distinguished from their + bodies. + + +
+ Function Types + + If Ft is a constrained function type Ft_1 when Fc, + where Ft_1 is a function type and + Fc is a function constraint, then Rep(T) = + {type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}. + If Ft is a function type (A_1, ..., A_n) -> B, + where each A_i and B are types, then + Rep(Ft) = {type,LINE,'fun',[{type,LINE,product,[Rep(A_1), + ..., Rep(A_n)]},Rep(B)]}. + +
+ +
+ Function Constraints +

A function constraint Fc is a nonempty sequence of constraints + C_1, ..., C_k, and + Rep(Fc) = [Rep(C_1), ..., Rep(C_k)].

+ + If C is a constraint is_subtype(V, T) or V :: T, + where V is a type variable and T is a type, then + Rep(C) = {type,LINE,constraint,[Rep(F),[Rep(V),Rep(T)]]}. + + +
+
+ +
+ The Abstract Format After Preprocessing

The compilation option can be given to the compiler to have the abstract code stored in the chunk in the BEAM file -- cgit v1.2.3 From af1e0396fda62efb6c0817134b419b166b110d09 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 1 Dec 2015 12:45:03 +0100 Subject: erts: Remove CDATA from The Abstract Format document --- erts/doc/src/absform.xml | 579 +++++++++++++++++++++++------------------------ 1 file changed, 288 insertions(+), 291 deletions(-) diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 8584898a9d..ca06794a53 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -11,7 +11,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + The Abstract Format @@ -35,24 +35,24 @@

This document describes the standard representation of parse trees for Erlang programs as Erlang terms. This representation is known as the abstract format. - Functions dealing with such parse trees are + Functions dealing with such parse trees are compile:forms/[1,2] and functions in the modules - , - , - , - , - , + epp, + erl_eval, + erl_lint, + erl_pp, + erl_parse, and - . + io. They are also used as input and output for parse transforms (see the module - ).

-

We use the function to denote the mapping from an Erlang source - construct to its abstract format representation , and write - . + compile).

+

We use the function Rep to denote the mapping from an Erlang source + construct C to its abstract format representation R, and write + R = Rep(C).

-

The word below represents an integer, and denotes the +

The word LINE below represents an integer, and denotes the number of the line in the source file where the construction occurred. - Several instances of in the same construction may denote + Several instances of LINE in the same construction may denote different lines.

Since operators are not terms in their own right, when operators are mentioned below, the representation of an operator should be taken to @@ -66,24 +66,24 @@ function declarations or attributes.

If D is a module declaration consisting of the forms - , ..., , then - Rep(D) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . - If F is an attribute , then - Rep(F) = . + F_1, ..., F_k, then + Rep(D) = [Rep(F_1), ..., Rep(F_k)]. + If F is an attribute -module(Mod), then + Rep(F) = {attribute,LINE,module,Mod}. + If F is an attribute -behavior(Behavior), then + Rep(F) = {attribute,LINE,behavior,Behavior}. + If F is an attribute -behaviour(Behaviour), then + Rep(F) = {attribute,LINE,behaviour,Behaviour}. + If F is an attribute -export([Fun_1/A_1, ..., Fun_k/A_k]), then + Rep(F) = {attribute,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}. + If F is an attribute -import(Mod,[Fun_1/A_1, ..., Fun_k/A_k]), then + Rep(F) = {attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}. + If F is an attribute -export_type([Type_1/A_1, ..., Type_k/A_k]), then + Rep(F) = {attribute,LINE,export_type,[{Type_1,A_1}, ..., {Type_k,A_k}]}. + If F is an attribute -compile(Options), then + Rep(F) = {attribute,LINE,compile,Options}. + If F is an attribute -file(File,Line), then + Rep(F) = {attribute,LINE,file,{File,Line}}. If F is a record declaration -record(Name,{V_1, ..., V_k}), then Rep(F) = {attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}. @@ -109,8 +109,8 @@ Arity, then Rep(F) = {attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}. - If F is a wild attribute , then - Rep(F) = . + If F is a wild attribute -A(T), then + Rep(F) = {attribute,LINE,A,T}.

If F is a function declaration Name Fc_1 ; ... ; Name Fc_k, @@ -126,11 +126,11 @@ explicit default initializer expression, as well as an optional type.

- If V is , then - Rep(V) = . - If V is , + If V is A, then + Rep(V) = {record_field,LINE,Rep(A)}. + If V is A = E, where E is an expression, then - Rep(V) = . + Rep(V) = {record_field,LINE,Rep(A),Rep(E)}. If V is A :: T, where T is a type and it does not contain undefined syntactically, then Rep(V) = @@ -163,14 +163,14 @@ same way in patterns, expressions and guards:

If L is an integer or character literal, then - Rep(L) = . + Rep(L) = {integer,LINE,L}.
If L is a float literal, then - Rep(L) = . + Rep(L) = {float,LINE,L}.
If L is a string literal consisting of the characters - , ..., , then - Rep(L) = . + C_1, ..., C_k, then + Rep(L) = {string,LINE,[C_1, ..., C_k]}.
If L is an atom literal, then - Rep(L) = . + Rep(L) = {atom,LINE,L}.

Note that negative integer and float literals do not occur as such; they are parsed as an application of the unary negation operator.

@@ -178,46 +178,46 @@
Patterns -

If is a sequence of patterns , then - Rep(Ps) = . Such sequences occur as the +

If Ps is a sequence of patterns P_1, ..., P_k, then + Rep(Ps) = [Rep(P_1), ..., Rep(P_k)]. Such sequences occur as the list of arguments to a function or fun.

Individual patterns are represented as follows:

If P is an atomic literal L, then Rep(P) = Rep(L). - If P is a compound pattern , then - Rep(P) = . - If P is a variable pattern , then - Rep(P) = , + If P is a compound pattern P_1 = P_2, then + Rep(P) = {match,LINE,Rep(P_1),Rep(P_2)}. + If P is a variable pattern V, then + Rep(P) = {var,LINE,A}, where A is an atom with a printname consisting of the same characters as - . - If P is a universal pattern , then - Rep(P) = . - If P is a tuple pattern , then - Rep(P) = . - If P is a nil pattern , then - Rep(P) = . - If P is a cons pattern , then - Rep(P) = . - If E is a binary pattern >]]>, then - Rep(E) = . + V. + If P is a universal pattern _, then + Rep(P) = {var,LINE,'_'}. + If P is a tuple pattern {P_1, ..., P_k}, then + Rep(P) = {tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}. + If P is a nil pattern [], then + Rep(P) = {nil,LINE}. + If P is a cons pattern [P_h | P_t], then + Rep(P) = {cons,LINE,Rep(P_h),Rep(P_t)}. + If E is a binary pattern <<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>>, then + Rep(E) = {bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}. For Rep(TSL), see below. - An omitted is represented by . An omitted - (type specifier list) is represented by . - If P is , where is a binary operator (this - is either an occurrence of applied to a literal string or character + An omitted Size is represented by default. An omitted TSL + (type specifier list) is represented by default. + If P is P_1 Op P_2, where Op is a binary operator (this + is either an occurrence of ++ applied to a literal string or character list, or an occurrence of an expression that can be evaluated to a number at compile time), - then Rep(P) = . - If P is , where is a unary operator (this is an + then Rep(P) = {op,LINE,Op,Rep(P_1),Rep(P_2)}. + If P is Op P_0, where Op is a unary operator (this is an occurrence of an expression that can be evaluated to a number at compile - time), then Rep(P) = . - If P is a record pattern , + time), then Rep(P) = {op,LINE,Op,Rep(P_0)}. + If P is a record pattern #Name{Field_1=P_1, ..., Field_k=P_k}, then Rep(P) = - . - If P is , then - Rep(P) = . - If P is , then - Rep(P) = , + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}. + If P is #Name.Field, then + Rep(P) = {record_index,LINE,Name,Rep(Field)}. + If P is ( P_0 ), then + Rep(P) = Rep(P_0), that is, patterns cannot be distinguished from their bodies.

Note that every pattern has the same source form as some expression, and is @@ -226,159 +226,153 @@

Expressions -

A body B is a sequence of expressions , and - Rep(B) = .

+

A body B is a sequence of expressions E_1, ..., E_k, and + Rep(B) = [Rep(E_1), ..., Rep(E_k)].

An expression E is one of the following alternatives:

- If P is an atomic literal , then - Rep(P) = Rep(L). - If E is , then - Rep(E) = . - If E is a variable , then - Rep(E) = , - where is an atom with a printname consisting of the same - characters as . - If E is a tuple skeleton , then - Rep(E) = . - If E is , then - Rep(E) = . - If E is a cons skeleton , then - Rep(E) = . - If E is a binary constructor >]]>, then - Rep(E) = . + If P is an atomic literal L, then Rep(P) = Rep(L). + If E is P = E_0, then + Rep(E) = {match,LINE,Rep(P),Rep(E_0)}. + If E is a variable V, then Rep(E) = {var,LINE,A}, + where A is an atom with a printname consisting of the same + characters as V. + If E is a tuple skeleton {E_1, ..., E_k}, then + Rep(E) = {tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}. + If E is [], then + Rep(E) = {nil,LINE}. + If E is a cons skeleton [E_h | E_t], then + Rep(E) = {cons,LINE,Rep(E_h),Rep(E_t)}. + If E is a binary constructor <<V_1:Size_1/TSL_1, ..., V_k:Size_k/TSL_k>>, then Rep(E) = + {bin,LINE,[{bin_element,LINE,Rep(V_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(V_k),Rep(Size_k),Rep(TSL_k)}]}. For Rep(TSL), see below. - An omitted is represented by . An omitted - (type specifier list) is represented by . - If E is , where is a binary operator, - then Rep(E) = . - If E is , where is a unary operator, then - Rep(E) = . - If E is , then - Rep(E) = - . - If E is , then - Rep(E) = - . - If E is , then - Rep(E) = . - If E is , then - Rep(E) = . - If E is where each - is a map assoc or exact field, then Rep(E) = - . For Rep(W), see - below. - If E is where - is a map assoc or exact field, then Rep(E) = - . For - Rep(W), see below. - If E is , then - Rep(E) = . - If E is , then - Rep(E) = . - If E is , then + An omitted Size is represented by default. An omitted TSL + (type specifier list) is represented by default. + If E is E_1 Op E_2, where Op is a binary operator, + then Rep(E) = {op,LINE,Op,Rep(E_1),Rep(E_2)}. + If E is Op E_0, where Op is a unary operator, then + Rep(E) = {op,LINE,Op,Rep(E_0)}. + If E is #Name{Field_1=E_1, ..., Field_k=E_k}, + then Rep(E) = + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. + If E is E_0#Name{Field_1=E_1, ..., Field_k=E_k}, then Rep(E) = - . - If E is a list comprehension , - where each is a generator or a filter, then - Rep(E) = . For Rep(W), see + {record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}. + If E is #Name.Field, then + Rep(E) = {record_index,LINE,Name,Rep(Field)}. + If E is E_0#Name.Field, then + Rep(E) = {record_field,LINE,Rep(E_0),Name,Rep(Field)}. + If E is #{W_1, ..., W_k} where each + W_i is a map assoc or exact field, then Rep(E) = + {map,LINE,[Rep(W_1), ..., Rep(W_k)]}. For Rep(W), see below. - If E is a binary comprehension >]]>, - where each is a generator or a filter, then - Rep(E) = . For Rep(W), see + If E is E_0#{W_1, ..., W_k} where + W_i is a map assoc or exact field, then Rep(E) = + {map,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. + For Rep(W), see below. + If E is catch E_0, then + Rep(E) = {'catch',LINE,Rep(E_0)}. + If E is E_0(E_1, ..., E_k), then + Rep(E) = {call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}. + If E is E_m:E_0(E_1, ..., E_k), then Rep(E) = + {call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}. + + If E is a list comprehension [E_0 || W_1, ..., W_k], + where each W_i is a generator or a filter, then Rep(E) = + {lc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. For Rep(W), see below. - If E is , where is a body, then - Rep(E) = . - If E is , - where each is an if clause then - Rep(E) = - . - If E is , - where is an expression and each is a - case clause then - Rep(E) = - . - If E is , - where is a body and each is a catch clause then + If E is a binary comprehension + <<E_0 || W_1, ..., W_k>>, + where each W_i is a generator or a filter, then + Rep(E) = {bc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}. + For Rep(W), see below. + If E is begin B end, where B is a body, then + Rep(E) = {block,LINE,Rep(B)}. + If E is if Ic_1 ; ... ; Ic_k end, + where each Ic_i is an if clause then Rep(E) = + {'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}. + If E is case E_0 of Cc_1 ; ... ; Cc_k end, + where E_0 is an expression and each Cc_i is a + case clause then Rep(E) = + {'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}. + If E is try B catch Tc_1 ; ... ; Tc_k end, + where B is a body and each Tc_i is a catch clause then Rep(E) = - . - If E is , - where is a body, - each is a case clause and - each is a catch clause then + {'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}. + If E is try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end, + where B is a body, + each Cc_i is a case clause and + each Tc_j is a catch clause then Rep(E) = + {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}. + If E is try B after A end, + where B and A are bodies then Rep(E) = + {'try',LINE,Rep(B),[],[],Rep(A)}. + If E is try B of Cc_1 ; ... ; Cc_k after A end, + where B and A are a bodies and + each Cc_i is a case clause then Rep(E) = + {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[],Rep(A)}. + If E is try B catch Tc_1 ; ... ; Tc_k after A end, + where B and A are bodies and + each Tc_i is a catch clause then Rep(E) = + {'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}. + If E is try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end, + where B and A are a bodies, + each Cc_i is a case clause and + each Tc_j is a catch clause then Rep(E) = - . - If E is , - where and are bodies then - Rep(E) = - . - If E is , - where and are a bodies and - each is a case clause then - Rep(E) = - . - If E is , - where and are bodies and - each is a catch clause then - Rep(E) = - . - If E is , - where and are a bodies, - each is a case clause and - each is a catch clause then - Rep(E) = - . - If E is , - where each is a case clause then - Rep(E) = - . - If E is B_t end]]>, - where each is a case clause, - is an expression and is a body, then - Rep(E) = - . - If E is , then - Rep(E) = . - If E is , then - Rep(E) = . - (Before the R15 release: Rep(E) = .) - If E is - where each is a function clause then Rep(E) = - . - If E is - where is a variable and each - is a function clause then Rep(E) = - . + {'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}. + If E is receive Cc_1 ; ... ; Cc_k end, + where each Cc_i is a case clause then Rep(E) = + {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}. + If E is receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end, + where each Cc_i is a case clause, + E_0 is an expression and B_t is a body, then Rep(E) = + {'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}. + If E is fun Name / Arity, then + Rep(E) = {'fun',LINE,{function,Name,Arity}}. + If E is fun Module:Name/Arity, then Rep(E) = + {'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}. + (Before the R15 release: Rep(E) = + {'fun',LINE,{function,Module,Name,Arity}}.) + If E is fun Fc_1 ; ... ; Fc_k end + where each Fc_i is a function clause then Rep(E) = + {'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}. + If E is fun Name Fc_1 ; ... ; Name Fc_k end + where Name is a variable and each + Fc_i is a function clause then Rep(E) = + {named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}. - If E is , then + If E is ( E_0 ), then Rep(E) = Rep(E_0), that is, parenthesized expressions cannot be distinguished from their bodies.
Generators and Filters -

When W is a generator or a filter (in the body of a list or binary comprehension), then:

+

When W is a generator or a filter (in the body of a list or + binary comprehension), then:

- If W is a generator , where is a pattern and - is an expression, then - Rep(W) = . - If W is a generator , where is a pattern and - is an expression, then - Rep(W) = . - If W is a filter , which is an expression, then - Rep(W) = . + If W is a generator P <- E, where P is + a pattern and E is an expression, then + Rep(W) = {generate,LINE,Rep(P),Rep(E)}. + If W is a generator P <= E, where P is + a pattern and E is an expression, then + Rep(W) = {b_generate,LINE,Rep(P),Rep(E)}. + If W is a filter E, which is an expression, then + Rep(W) = Rep(E).
Binary Element Type Specifiers

A type specifier list TSL for a binary element is a sequence of type - specifiers . - Rep(TSL) = .

+ specifiers TS_1 - ... - TS_k. + Rep(TSL) = [Rep(TS_1), ..., Rep(TS_k)].

When TS is a type specifier for a binary element, then:

- If TS is an atom , then Rep(TS) = . - If TS is a couple where is an atom and - is an integer, then Rep(TS) = {A,Value}. + If TS is an atom A, then Rep(TS) = A. + If TS is a couple A:Value where A is an atom + and Value is an integer, then Rep(TS) = + {A,Value}.
@@ -386,13 +380,13 @@ Map Assoc and Exact Fields

When W is an assoc or exact field (in the body of a map), then:

- If W is an assoc field V]]>, where - and are both expressions, - then Rep(W) = . + If W is an assoc field K => V, where + K and V are both expressions, + then Rep(W) = {map_field_assoc,LINE,Rep(K),Rep(V)}. - If W is an exact field , where - and are both expressions, - then Rep(W) = . + If W is an exact field K := V, where + K and V are both expressions, + then Rep(W) = {map_field_exact,LINE,Rep(K),Rep(V)}.
@@ -400,92 +394,95 @@
Clauses -

There are function clauses, if clauses, case clauses +

There are function clauses, if clauses, case clauses and catch clauses.

-

A clause is one of the following alternatives:

+

A clause C is one of the following alternatives:

- If C is a function clause B]]> - where is a pattern sequence and is a body, then - Rep(C) = . - If C is a function clause B]]> - where is a pattern sequence, - is a guard sequence and is a body, then - Rep(C) = . - If C is an if clause B]]> - where is a guard sequence and is a body, then - Rep(C) = . - If C is a case clause B]]> - where is a pattern and is a body, then - Rep(C) = . - If C is a case clause B]]> - where is a pattern, - is a guard sequence and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is a pattern and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is an atomic literal or a variable pattern, - is a pattern and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is a pattern, is a guard sequence - and is a body, then - Rep(C) = . - If C is a catch clause B]]> - where is an atomic literal or a variable pattern, - is a pattern, is a guard sequence - and is a body, then - Rep(C) = . + If C is a function clause ( Ps ) -> B + where Ps is a pattern sequence and B is a body, then + Rep(C) = {clause,LINE,Rep(Ps),[],Rep(B)}. + If C is a function clause ( Ps ) when Gs -> B + where Ps is a pattern sequence, + Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}. + If C is an if clause Gs -> B + where Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,[],Rep(Gs),Rep(B)}. + If C is a case clause P -> B + where P is a pattern and B is a body, then + Rep(C) = {clause,LINE,[Rep(P)],[],Rep(B)}. + If C is a case clause P when Gs -> B + where P is a pattern, + Gs is a guard sequence and B is a body, then + Rep(C) = {clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}. + If C is a catch clause P -> B + where P is a pattern and B is a body, then + Rep(C) = {clause,LINE,[Rep({throw,P,_})],[],Rep(B)}. + If C is a catch clause X : P -> B + where X is an atomic literal or a variable pattern, + P is a pattern and B is a body, then + Rep(C) = {clause,LINE,[Rep({X,P,_})],[],Rep(B)}. + If C is a catch clause P when Gs -> B + where P is a pattern, Gs is a guard sequence + and B is a body, then + Rep(C) = {clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}. + If C is a catch clause X : P when Gs -> B + where X is an atomic literal or a variable pattern, + P is a pattern, Gs is a guard sequence + and B is a body, then + Rep(C) = {clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}.
Guards -

A guard sequence Gs is a sequence of guards , and - Rep(Gs) = . If the guard sequence is - empty, Rep(Gs) = .

-

A guard G is a nonempty sequence of guard tests , and - Rep(G) = .

-

A guard test is one of the following alternatives:

+

A guard sequence Gs is a sequence of guards G_1; ...; G_k, and + Rep(Gs) = [Rep(G_1), ..., Rep(G_k)]. If the guard sequence is + empty, Rep(Gs) = [].

+

A guard G is a nonempty sequence of guard tests + Gt_1, ..., Gt_k, and Rep(G) = + [Rep(Gt_1), ..., Rep(Gt_k)].

+

A guard test Gt is one of the following alternatives:

If Gt is an atomic literal L, then Rep(Gt) = Rep(L). - If Gt is a variable pattern , then - Rep(Gt) = , - where A is an atom with a printname consisting of the same characters as - . - If Gt is a tuple skeleton , then - Rep(Gt) = . - If Gt is , then - Rep(Gt) = . - If Gt is a cons skeleton , then - Rep(Gt) = . - If Gt is a binary constructor >]]>, then - Rep(Gt) = . + If Gt is a variable pattern V, then + Rep(Gt) = {var,LINE,A}, where A is an atom with + a printname consisting of the same characters as V. + If Gt is a tuple skeleton {Gt_1, ..., Gt_k}, then + Rep(Gt) = {tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is [], then Rep(Gt) = {nil,LINE}. + If Gt is a cons skeleton [Gt_h | Gt_t], then + Rep(Gt) = {cons,LINE,Rep(Gt_h),Rep(Gt_t)}. + If Gt is a binary constructor + <<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>>, then + Rep(Gt) = {bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}. For Rep(TSL), see above. - An omitted is represented by . An omitted - (type specifier list) is represented by . - If Gt is , where - is a binary operator, then Rep(Gt) = . - If Gt is , where is a unary operator, then - Rep(Gt) = . - If Gt is , then + An omitted Size is represented by default. + An omitted TSL (type specifier list) is represented + by default. + If Gt is Gt_1 Op Gt_2, where Op + is a binary operator, then Rep(Gt) = + {op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}. + If Gt is Op Gt_0, where Op is a unary operator, then + Rep(Gt) = {op,LINE,Op,Rep(Gt_0)}. + If Gt is #Name{Field_1=Gt_1, ..., Field_k=Gt_k}, then Rep(E) = - . - If Gt is , then - Rep(Gt) = . - If Gt is , then - Rep(Gt) = . - If Gt is , where is an atom, then - Rep(Gt) = . - If Gt is , where is - the atom and is an atom or an operator, then - Rep(Gt) = . - If Gt is , where is - the atom and is an atom or an operator, then - Rep(Gt) = . - If Gt is , then - Rep(Gt) = , that is, parenthesized + {record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}. + If Gt is #Name.Field, then + Rep(Gt) = {record_index,LINE,Name,Rep(Field)}. + If Gt is Gt_0#Name.Field, then + Rep(Gt) = {record_field,LINE,Rep(Gt_0),Name,Rep(Field)}. + If Gt is A(Gt_1, ..., Gt_k), where A is an atom, then + Rep(Gt) = {call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is A_m:A(Gt_1, ..., Gt_k), where A_m is + the atom erlang and A is an atom or an operator, then + Rep(Gt) = {call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}. + If Gt is {A_m,A}(Gt_1, ..., Gt_k), where A_m is + the atom erlang and A is an atom or an operator, then + Rep(Gt) = {call,LINE,Rep({A_m,A}),[Rep(Gt_1), ..., Rep(Gt_k)]}. + + If Gt is ( Gt_0 ), then + Rep(Gt) = Rep(Gt_0), that is, parenthesized guard tests cannot be distinguished from their bodies.

Note that every guard test has the same source form as some expression, @@ -599,18 +596,18 @@

The Abstract Format After Preprocessing -

The compilation option can be given to the - compiler to have the abstract code stored in - the chunk in the BEAM file +

The compilation option debug_info can be given to the + compiler to have the abstract code stored in + the abstract_code chunk in the BEAM file (for debugging purposes).

-

In OTP R9C and later, the chunk will +

In OTP R9C and later, the abstract_code chunk will contain

-

-

where is the abstract code as described +

{raw_abstract_v1,AbstractCode}

+

where AbstractCode is the abstract code as described in this document.

In releases of OTP prior to R9C, the abstract code after some more processing was stored in the BEAM file. The first element of the - tuple would be either (R7B) or + tuple would be either abstract_v1 (R7B) or abstract_v2 (R8B).

-- cgit v1.2.3 From 23b158133466f3fd60336672e11f43ffefb0c941 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 1 Dec 2015 10:59:04 +0100 Subject: doc: Fix some minor issues in Types and Function Specifications --- system/doc/reference_manual/typespec.xml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml index 7891f3a6b9..22627058c1 100644 --- a/system/doc/reference_manual/typespec.xml +++ b/system/doc/reference_manual/typespec.xml @@ -197,6 +197,9 @@ char()0..16#10ffff + nil()[] + + number()integer() | float() @@ -312,7 +315,7 @@

As seen, the basic syntax of a type is an atom followed by closed parentheses. New types are declared using -type and -opaque - compiler attributes as in the following: + attributes as in the following:

   -type my_struct_type() :: Type.
@@ -435,8 +438,8 @@
   
Specifications for Functions

- A specification (or contract) for a function is given using the new - compiler attribute -spec. The general format is as follows: + A specification (or contract) for a function is given using the + -spec attribute. The general format is as follows:

   -spec Module:Function(ArgType1, ..., ArgTypeN) -> ReturnType.
@@ -451,7 +454,7 @@ explicitly) import these functions.

- Within a given module, the following shorthand suffice in most cases: + Within a given module, the following shorthand suffices in most cases:

   -spec Function(ArgType1, ..., ArgTypeN) -> ReturnType.
-- cgit v1.2.3