Chapter 12 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 [when expr]
 

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

attribute::= [@ attr-id attr-payload ]
 
expr::= ...
  expr attribute
 
typexpr::= ...
  typexpr attribute
 
pattern::= ...
  pattern attribute
 
module-expr::= ...
  module-expr attribute
 
module-type::= ...
  module-type attribute
 
class-expr::= ...
  class-expr attribute
 
class-type::= ...
  class-type attribute
 

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()) [ of constr-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-id attr-payload ]
 
typedef::= ...
  typedef item-attribute
 
exception-definition::= exception constr-decl
  exception constr-name = constr
 
module-items::= [;;] ( definitionexpr { item-attribute } ) { [;;] definition;; expr { item-attribute } } [;;]
 
class-binding::= ...
  class-binding item-attribute
 
class-spec::= ...
  class-spec item-attribute
 
classtype-def::= ...
  classtype-def item-attribute
 
let-binding::= ...
  let-binding item-attribute
 
definition::= let [rec] let-binding { and let-binding }
  external value-name : typexpr = external-declaration { item-attribute }
  type-definition
  exception-definition { item-attribute }
  class-definition
  classtype-definition
  module module-name { ( module-name : module-type ) } [ : module-type ] = module-expr { item-attribute }
  module type modtype-name = module-type { item-attribute }
  open module-path { item-attribute }
  include module-expr { item-attribute }
  module rec module-name : module-type = module-expr { item-attribute } { and module-name : module-type = module-expr { item-attribute } }
 
specification::= val value-name : typexpr { item-attribute }
  external value-name : typexpr = external-declaration { item-attribute }
  type-definition
  exception constr-decl { item-attribute }
  class-specification
  classtype-definition
  module module-name : module-type { item-attribute }
  module module-name { ( module-name : module-type ) } : module-type { item-attribute }
  module type modtype-name { item-attribute }
  module type modtype-name = module-type { item-attribute }
  open module-path { item-attribute }
  include module-type { item-attribute }
 
class-field-spec::= ...
  class-field-spec item-attribute
 
class-field::= ...
  class-field item-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-id attr-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 compiler:

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 13.5.3) 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 13.5.3) 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