Chapter 10 Language extensions

12 Attributes

(Introduced in OCaml 4.02, infix notations for constructs other than expressions added in 4.03)

Attributes are “decorations” of the syntax tree which are mostly ignored by the type-checker but can be used by external tools. An attribute is made of an identifier and a payload, which can be a structure, a type expression (prefixed with :), a signature (prefixed with :) or a pattern (prefixed with ?) optionally followed by a when clause:

attr-id::= lowercase-ident
   capitalized-ident
   attr-id.attr-id
 
attr-payload::= [ module-items ]
   :typexpr
   : [ specification ]
   ?pattern [whenexpr]
 

The first form of attributes is attached with a postfix notation on “algebraic” categories:

attribute::= [@attr-idattr-payload]
 
expr::= ...
 exprattribute
 
typexpr::= ...
 typexprattribute
 
pattern::= ...
 patternattribute
 
module-expr::= ...
 module-exprattribute
 
module-type::= ...
 module-typeattribute
 
class-expr::= ...
 class-exprattribute
 
class-type::= ...
 class-typeattribute
 

This form of attributes can also be inserted after the `tag-name in polymorphic variant type expressions (tag-spec-first, tag-spec, tag-spec-full) or after the method-name in method-type.

The same syntactic form is also used to attach attributes to labels and constructors in type declarations:

field-decl::= [mutable] field-name:poly-typexpr { attribute }
 
constr-decl::= (constr-name ∣ ()) [ ofconstr-args ] { attribute }
 

Note: when a label declaration is followed by a semi-colon, attributes can also be put after the semi-colon (in which case they are merged to those specified before).

The second form of attributes are attached to “blocks” such as type declarations, class fields, etc:

item-attribute::= [@@attr-idattr-payload]
 
typedef::= ...
 typedefitem-attribute
 
exception-definition::= exceptionconstr-decl
 exceptionconstr-name=constr
 
module-items::= [;;] ( definition ∣ expr { item-attribute } ) { [;;] definition ∣ ;;expr { item-attribute } } [;;]
 
class-binding::= ...
 class-bindingitem-attribute
 
class-spec::= ...
 class-specitem-attribute
 
classtype-def::= ...
 classtype-defitem-attribute
 
definition::= let [rec] let-binding { andlet-binding }
 externalvalue-name:typexpr=external-declaration { item-attribute }
 type-definition
 exception-definition { item-attribute }
 class-definition
 classtype-definition
 modulemodule-name { (module-name:module-type) } [ :module-type ]  =module-expr { item-attribute }
 moduletypemodtype-name=module-type { item-attribute }
 openmodule-path { item-attribute }
 includemodule-expr { item-attribute }
 modulerecmodule-name:module-type=module-expr { item-attribute }  { andmodule-name:module-type=module-expr  { item-attribute } }
 
specification::= valvalue-name:typexpr { item-attribute }
 externalvalue-name:typexpr=external-declaration { item-attribute }
 type-definition
 exceptionconstr-decl { item-attribute }
 class-specification
 classtype-definition
 modulemodule-name:module-type { item-attribute }
 modulemodule-name { (module-name:module-type) } :module-type { item-attribute }
 moduletypemodtype-name { item-attribute }
 moduletypemodtype-name=module-type { item-attribute }
 openmodule-path { item-attribute }
 includemodule-type { item-attribute }
 
class-field-spec::= ...
 class-field-specitem-attribute
 
class-field::= ...
 class-fielditem-attribute
 

A third form of attributes appears as stand-alone structure or signature items in the module or class sub-languages. They are not attached to any specific node in the syntax tree:

floating-attribute::= [@@@attr-idattr-payload]
 
definition::= ...
 floating-attribute
 
specification::= ...
 floating-attribute
 
class-field-spec::= ...
 floating-attribute
 
class-field::= ...
 floating-attribute
 

(Note: contrary to what the grammar above describes, item-attributes cannot be attached to these floating attributes in class-field-spec and class-field.)

It is also possible to specify attributes using an infix syntax. For instance:

let[@foo] x = 2 in x + 1          === (let x = 2 [@@foo] in x + 1)
begin[@foo][@bar x] ... end       === (begin ... end)[@foo][@bar x]
module[@foo] M = ...              === module M = ... [@@foo]
type[@foo] t = T                  === type t = T [@@foo]
method[@foo] m = ...              === method m = ... [@@foo]

For let, the attributes are applied to each bindings:

let[@foo] x = 2 and y = 3 in x + y === (let x = 2 [@@foo] and y = 3 in x + y)
let[@foo] x = 2
and[@bar] y = 3 in x + y           === (let x = 2 [@@foo] and y = 3 [@@bar] in x + y)

12.1 Built-in attributes

Some attributes are understood by the type-checker:

module X = struct [@@@warning "+9"] (* locally enable warning 9 in this structure *)end [@@deprecated "Please use module 'Y' instead."] let x = begin[@warning "+9"] […] end type t = A | B [@@deprecated "Please use type 's' instead."]
let fires_warning_22 x = assert (x >= 0) [@ppwarning "TODO: remove this later"]
Warning 22 [preprocessor]: TODO: remove this later
let rec is_a_tail_call = function | [] -> () | _ :: q -> (is_a_tail_call[@tailcall]) q let rec not_a_tail_call = function | [] -> [] | x :: q -> x :: (not_a_tail_call[@tailcall]) q
Warning 51 [wrong-tailcall-expectation]: expected tailcall
let f x = x [@@inline] let () = (f[@inlined]) ()
type fragile = | Int of int [@warn_on_literal_pattern] | String of string [@warn_on_literal_pattern]
let fragile_match_1 = function | Int 0 -> () | _ -> ()
Warning 52 [fragile-literal-pattern]: Code should not depend on the actual values of this constructor's arguments. They are only for information and may change in future versions. (See manual section 11.5) val fragile_match_1 : fragile -> unit = <fun>
let fragile_match_2 = function | String "constant" -> () | _ -> ()
Warning 52 [fragile-literal-pattern]: Code should not depend on the actual values of this constructor's arguments. They are only for information and may change in future versions. (See manual section 11.5) val fragile_match_2 : fragile -> unit = <fun>
module Immediate: sig type t [@@immediate] val x: t ref end = struct type t = A | B let x = ref A end
module Int_or_int64 : sig type t [@@immediate64] val zero : t val one : t val add : t -> t -> t end = struct include Sys.Immediate64.Make(Int)(Int64) module type S = sig val zero : t val one : t val add : t -> t -> t end let impl : (module S) = match repr with | Immediate -> (module Int : S) | Non_immediate -> (module Int64 : S) include (val impl : S) end