8.13 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 mostlyignored by the type-checker but can be used by external tools. Anattribute is made of an identifier and a payload, which can be astructure, a type expression (prefixed with :), a signature(prefixed with :) or a pattern (prefixed with ?) optionallyfollowed 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-namein 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 andconstructors 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, attributescan also be put after the semi-colon (in which case they are merged tothose specified before).

The second form of attributes are attached to “blocks” such as typedeclarations, 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
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 orsignature items in the module or class sub-languages. They are notattached 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-attributescannot be attached to these floating attributes in class-field-specand class-field.)

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

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

For let, the attributes are applied to each bindings:

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

8.13.1 Built-in attributes

Some attributes are understood by the type-checker:

  • “ocaml.warning” or “warning”, with a string literal payload.This can be used as floating attributes in asignature/structure/object/object type. The string is parsed and hasthe same effect as the -w command-line option, in the scope betweenthe attribute and the end of the currentsignature/structure/object/object type. The attribute can also beattached to any kind of syntactic item which support attributes(such as an expression, or a type expression)in which case its scope is limited to that item.Note that it is not well-defined which scope is used for a specificwarning. This is implementation dependent and can change between versions.Some warnings are even completely outside the control of “ocaml.warning”(for instance, warnings 1, 2, 14, 29 and 50).
  • “ocaml.warnerror” or “warnerror”, with a string literal payload.Same as “ocaml.warning”, for the -warn-error command-line option.
  • “ocaml.alert” or “alert”: see section 8.22.
  • “ocaml.deprecated” or “deprecated”: alias for the“deprecated” alert, see section 8.22.
  • “ocaml.deprecated_mutable” or “deprecated_mutable”.Can be applied to a mutable record label. If the label is laterused to modify the field (with “expr.l <- expr”), the “deprecated” alertwill be triggered. If the payload of the attribute is a string literal,the alert message includes this text.
  • “ocaml.ppwarning” or “ppwarning”, in any context, witha string literal payload. The text is reported as warning (22)by the compiler (currently, the warning location is the locationof the string payload). This is mostly useful for preprocessors whichneed to communicate warnings to the user. This could also be usedto mark explicitly some code location for further inspection.
  • “ocaml.warn_on_literal_pattern” or “warn_on_literal_pattern” annotateconstructors in type definition. A warning (52) is then emitted when thisconstructor is pattern matched with a constant literal as argument. Thisattribute denotes constructors whose argument is purely informative andmay change in the future. Therefore, pattern matching on this argumentwith a constant literal is unreliable. For instance, all built-in exceptionconstructors are marked as “warn_on_literal_pattern”.Note that, due to an implementation limitation, this warning (52) is onlytriggered for single argument constructor.
  • “ocaml.tailcall” or “tailcall” can be applied to functionapplication in order to check that the call is tailcall optimized.If it it not the case, a warning (51) is emitted.
  • “ocaml.inline” or “inline” take either “never”, “always”or nothing as payload on a function or functor definition. If no payloadis provided, the default value is “always”. This payload controls whenapplications of the annotated functions should be inlined.
  • “ocaml.inlined” or “inlined” can be applied to any function or functorapplication to check that the call is inlined by the compiler. If the callis not inlined, a warning (55) is emitted.
  • “ocaml.noalloc”, “ocaml.unboxed”and “ocaml.untagged” or“noalloc”, “unboxed” and “untagged” can be used on externaldefinitions to obtain finer control over the C-to-OCaml interface. See19.11 for more details.
  • “ocaml.immediate” or “immediate” applied on an abstract type mark the type ashaving a non-pointer implementation (e.g. “int”, “bool”, “char” orenumerated types). Mutation of these immediate types does not activate thegarbage collector’s write barrier, which can significantly boost performance inprograms relying heavily on mutable state.
  • ocaml.unboxed or unboxed can be used on a type definition if thetype is a single-field record or a concrete type with a singleconstructor that has a single argument. It tells the compiler tooptimize the representation of the type by removing the block thatrepresents the record or the constructor (i.e. a value of this typeis physically equal to its argument). In the case of GADTs, anadditional restriction applies: the argument must not be anexistential variable, represented by an existential type variable,or an abstract type constructor applied to an existential typevariable.
  • ocaml.boxed or boxed can be used on type definitions to meanthe opposite of ocaml.unboxed: keep the unoptimizedrepresentation of the type. When there is no annotation, thedefault is currently boxed but it may change in the future.
  • ocaml.local or local take either never, always, maybe ornothing as payload on a function definition. If no payload isprovided, the default is always. The attribute controls anoptimization which consists in compiling a function into a staticcontinuation. Contrary to inlining, this optimization does notduplicate the function’s body. This is possible when allreferences to the function are full applications, all sharing thesame continuation (for instance, the returned value of severalbranches of a pattern matching). never disables the optimization,always asserts that the optimization applies (otherwise a warning55 is emitted) and maybe lets the optimization apply whenpossible (this is the default behavior when the attribute is notspecified). The optimization is implicitly disabled when using thebytecode compiler in debug mode (-g), and for functions marked withan ocaml.inline always or ocaml.unrolled attribute whichsupersede ocaml.local.
  1. module X = struct
  2. [@@@warning "+9"] (* locally enable warning 9 in this structure *)
  3. end
  4. [@@deprecated "Please use module 'Y' instead."]
  5. let x = begin[@warning "+9"] […] end
  6. type t = A | B
  7. [@@deprecated "Please use type 's' instead."]
  8.  
  1. let fires_warning_22 x =
  2. assert (x >= 0) [@ppwarning "TODO: remove this later"]
  3.  
  4. Warning 22: TODO: remove this later
  1. let rec is_a_tail_call = function
  2. | [] -> ()
  3. | _ :: q -> (is_a_tail_call[@tailcall]) q
  4. let rec not_a_tail_call = function
  5. | [] -> []
  6. | x :: q -> x :: (not_a_tail_call[@tailcall]) q
  7.  
  8. Warning 51: expected tailcall
  1. let f x = x [@@inline]
  2. let () = (f[@inlined]) ()
  3.  
  1. type fragile =
  2. | Int of int [@warn_on_literal_pattern]
  3. | String of string [@warn_on_literal_pattern]
  4.  
  1. let fragile_match_1 = function
  2. | Int 0 -> ()
  3. | _ -> ()
  4. Warning 52: Code should not depend on the actual values of
  5. this constructor's arguments. They are only for information
  6. and may change in future versions. (See manual section 9.5)
  7. val fragile_match_1 : fragile -> unit = <fun>
  1. let fragile_match_2 = function
  2. | String "constant" -> ()
  3. | _ -> ()
  4. Warning 52: Code should not depend on the actual values of
  5. this constructor's arguments. They are only for information
  6. and may change in future versions. (See manual section 9.5)
  7. val fragile_match_2 : fragile -> unit = <fun>
  1. module Immediate: sig
  2. type t [@@immediate]
  3. val x: t ref
  4. end = struct
  5. type t = A | B
  6. let x = ref A
  7. end
  8.