1"use strict";(globalThis.webpackChunkdocs=globalThis.webpackChunkdocs||[]).push([[1353],{29560(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>s,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"language/comptime","title":"Compile-time Code & Metaprogramming","description":"Learn how to use metaprogramming in Noir to create macros or derive your own traits","source":"@site/versioned_docs/version-v1.0.0-rc.0/language/comptime.md","sourceDirName":"language","slug":"/language/comptime","permalink":"/docs/v1.0.0-rc.0/language/comptime","draft":false,"unlisted":false,"editUrl":"https://github.com/noir-lang/noir/edit/master/docs/versioned_docs/version-v1.0.0-rc.0/language/comptime.md","tags":[],"version":"v1.0.0-rc.0","frontMatter":{"title":"Compile-time Code & Metaprogramming","description":"Learn how to use metaprogramming in Noir to create macros or derive your own traits","keywords":["Noir","comptime","compile-time","metaprogramming","macros","quote","unquote"]},"sidebar":"sidebar","previous":{"title":"Attributes","permalink":"/docs/v1.0.0-rc.0/language/attributes"},"next":{"title":"Black Box Functions","permalink":"/docs/v1.0.0-rc.0/libraries/standard_library/black_box_fns"}}');var o=t(74848),r=t(28453);const s={title:"Compile-time Code & Metaprogramming",description:"Learn how to use metaprogramming in Noir to create macros or derive your own traits",keywords:["Noir","comptime","compile-time","metaprogramming","macros","quote","unquote"]},a=void 0,l={},c=[{value:"Overview",id:"overview",level:2},{value:"Comptime",id:"comptime",level:2},{value:"Scoping",id:"scoping",level:3},{value:"Evaluating",id:"evaluating",level:3},{value:"Lowering",id:"lowering",level:3},{value:"(Quasi) Quote",id:"quasi-quote",level:2},{value:"Unquote",id:"unquote",level:2},{value:"Combining Tokens",id:"combining-tokens",level:3},{value:"$crate",id:"crate",level:3},{value:"Attributes",id:"attributes",level:2},{value:"Calling annotations with additional arguments",id:"calling-annotations-with-additional-arguments",level:3},{value:"Attribute Evaluation Order",id:"attribute-evaluation-order",level:3},{value:"Comptime API",id:"comptime-api",level:2},{value:"<code>#[use_callers_scope]</code>",id:"use_callers_scope",level:3},{value:"Example: Derive",id:"example-derive",level:2},{value:"Security considerations",id:"security-considerations",level:2},{value:"Comptime functions that return <code>Quoted</code>",id:"comptime-functions-that-return-quoted",level:3},{value:"<code>FunctionDefinition::disable</code>",id:"functiondefinitiondisable",level:3}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",sub:"sub",sup:"sup",ul:"ul",...(0,r.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,o.jsx)(n.p,{children:"Metaprogramming in Noir is comprised of three parts:"}),"\n",(0,o.jsxs)(n.ol,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime"})," code"]}),"\n",(0,o.jsx)(n.li,{children:"Quoting and unquoting"}),"\n",(0,o.jsxs)(n.li,{children:["The metaprogramming API in ",(0,o.jsx)(n.code,{children:"std::meta"})]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Each of these are explained in more detail in the next sections but the wide picture is that\n",(0,o.jsx)(n.code,{children:"comptime"})," allows us to write code which runs at compile-time. In this ",(0,o.jsx)(n.code,{children:"comptime"})," code we\ncan quote and unquote snippets of the program, manipulate them, and insert them in other\nparts of the program. Comptime functions which do this are said to be macros. Additionally,\nthere's a compile-time API of built-in types and functions provided by the compiler which allows\nfor greater analysis and modification of programs."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h2,{id:"comptime",children:"Comptime"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"comptime"})," is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime fn"})," to define functions which execute exclusively during compile-time."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime global"})," to define a global variable which is evaluated at compile-time.\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Unlike runtime globals, ",(0,o.jsx)(n.code,{children:"comptime global"}),"s can be mutable."]}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime struct"})," to define structs that refer to some comptime-only types"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime type"})," to define type aliases that refer to some comptime-only types"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime { ... }"})," to execute a block of statements during compile-time."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime let"})," to define a variable whose value is evaluated at compile-time."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"comptime for"})," to run a for loop at compile-time. Syntax sugar for ",(0,o.jsx)(n.code,{children:"comptime { for .. }"}),"."]}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"scoping",children:"Scoping"}),"\n",(0,o.jsxs)(n.p,{children:["Note that while in a ",(0,o.jsx)(n.code,{children:"comptime"})," context, any runtime variables ",(0,o.jsx)(n.em,{children:"local to the current function"})," are never visible."]}),"\n",(0,o.jsx)(n.h3,{id:"evaluating",children:"Evaluating"}),"\n",(0,o.jsxs)(n.p,{children:["Evaluation rules of ",(0,o.jsx)(n.code,{children:"comptime"})," follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Certain built-in functions may not be available, although more may be added over time."}),"\n",(0,o.jsxs)(n.li,{children:["Evaluation order of ",(0,o.jsx)(n.code,{children:"comptime {}"})," blocks within global items is currently unspecified. For example, given the following two functions we can't guarantee\nwhich ",(0,o.jsx)(n.code,{children:"println"})," will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same ",(0,o.jsx)(n.code,{children:"nargo"})," version as long as the program is also unchanged."]}),"\n"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:'fn one() {\n comptime { println("one"); }\n}\n\nfn two() {\n comptime { println("two"); }\n}\n'})}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering.\nFor example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided."}),"\n",(0,o.jsxs)(n.li,{children:["Although the ordering of comptime code is usually unspecified, there are cases where it is:\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Dependencies of a crate will always be evaluated before the dependent crate."}),"\n",(0,o.jsxs)(n.li,{children:["Any attributes on a function will be run before the function body is resolved. This is to allow the attribute to modify the function if necessary. Note that if the\nfunction itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify\nat compile-time, it may be helpful to sort your ",(0,o.jsx)(n.code,{children:"comptime"})," annotation functions into a different submodule crate along with any dependencies they require."]}),"\n",(0,o.jsxs)(n.li,{children:["Unlike raw ",(0,o.jsx)(n.code,{children:"comptime {}"})," blocks, attributes on top-level items in the program do have a set evaluation order. Attributes within a module are evaluated top-down, and attributes\nin different modules are evaluated submodule-first. Sibling modules to the same parent module are evaluated in order of the module declarations (",(0,o.jsx)(n.code,{children:"mod foo; mod bar;"}
1),") in their\nparent module."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"lowering",children:"Lowering"}),"\n",(0,o.jsxs)(n.p,{children:["When a ",(0,o.jsx)(n.code,{children:"comptime"})," value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"struct Foo { array: [Field; 2], len: u32 }\n\nfn main() {\n println(comptime {\n let mut foo = std::mem::zeroed::<Foo>();\n foo.array[0] = 4;\n foo.len = 1;\n foo\n });\n}\n"})}),"\n",(0,o.jsxs)(n.p,{children:["will be converted to the following after ",(0,o.jsx)(n.code,{children:"comptime"})," expressions are evaluated:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"struct Foo { array: [Field; 2], len: u32 }\n\nfn main() {\n println(Foo { array: [4, 0], len: 1 });\n}\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Not all types of values can be lowered. For example, references, ",(0,o.jsx)(n.code,{children:"Type"}),"s, and ",(0,o.jsx)(n.code,{children:"TypeDefinition"}),"s (among other types) cannot be lowered at all."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"fn main() {\n // There's nothing we could inline here to create a Type value at runtime\n // let _ = get_type!();\n}\n\ncomptime fn get_type() -> Type { ... }\n"})}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h2,{id:"quasi-quote",children:"(Quasi) Quote"}),"\n",(0,o.jsxs)(n.p,{children:["Macros in Noir are ",(0,o.jsx)(n.code,{children:"comptime"})," functions which return code as a value which is inserted into the call site when it is lowered there.\nA code value in this case is of type ",(0,o.jsx)(n.code,{children:"Quoted"})," and can be created by a ",(0,o.jsx)(n.code,{children:"quote { ... }"})," expression.\nMore specifically, the code value ",(0,o.jsx)(n.code,{children:"quote"})," creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators.\nFor example, the expression ",(0,o.jsx)(n.code,{children:'quote { Hi "there reader"! }'}),' would quote three tokens: the word "hi", the string "there reader", and an exclamation mark.\nYou\'ll note that snippets that would otherwise be invalid syntax can still be quoted.']}),"\n",(0,o.jsxs)(n.p,{children:["When a ",(0,o.jsx)(n.code,{children:"Quoted"})," value is used in runtime code, it is lowered into a ",(0,o.jsx)(n.code,{children:"quote { ... }"})," expression. Since this expression is only valid\nin compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the\nprogram at that point, and parse it as an expression. To do this, we have to add a ",(0,o.jsx)(n.code,{children:"!"})," after the function name returning the ",(0,o.jsx)(n.code,{children:"Quoted"})," value.\nIf the value was created locally and there is no function returning it, ",(0,o.jsx)(n.code,{children:"std::meta::unquote!(_)"})," can be used instead.\nCalling such a function at compile-time without ",(0,o.jsx)(n.code,{children:"!"})," will just return the ",(0,o.jsx)(n.code,{children:"Quoted"})," value to be further manipulated. For example:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="quote-example" showLineNumbers ',children:"comptime fn quote_one() -> Quoted {\n quote { 1 }\n}\n\n#[test]\nfn returning_versus_macro_insertion() {\n comptime {\n // let _a: Quoted = quote { 1 };\n let _a: Quoted = quote_one();\n\n // let _b: Field = 1;\n let _b: Field = quote_one!();\n\n // Since integers default to fields, if we\n // want a different type we have to explicitly cast\n // let _c: i32 = 1 as i32;\n let _c: i32 = quote_one!() as i32;\n }\n}\n"})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L158-L178",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L158-L178"})})})}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["For those familiar with quoting from other languages (primarily lisps), Noir's ",(0,o.jsx)(n.code,{children:"quote"})," is actually a ",(0,o.jsx)(n.em,{children:"quasiquote"}
1),".\nThis means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code."]}),"\n",(0,o.jsx)(n.p,{children:"In addition to curly braces, you can also use square braces for the quote operator:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"comptime {\n let q1 = quote { 1 };\n let q2 = quote [ 2 ];\n assert_eq(q1, q2);\n\n // Square braces can be used to quote mismatched curly braces if needed\n let _ = quote[}];\n}\n"})}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h2,{id:"unquote",children:"Unquote"}),"\n",(0,o.jsxs)(n.p,{children:["The unquote operator ",(0,o.jsx)(n.code,{children:"$"})," is usable within a ",(0,o.jsx)(n.code,{children:"quote"})," expression.\nIt takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example,"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"comptime {\n let x = 1 + 2;\n let y = quote { $x + 4 };\n}\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The value of ",(0,o.jsx)(n.code,{children:"y"})," above will be the token stream containing ",(0,o.jsx)(n.code,{children:"3"}),", ",(0,o.jsx)(n.code,{children:"+"}),", and ",(0,o.jsx)(n.code,{children:"4"}),". We can also use this to combine ",(0,o.jsx)(n.code,{children:"Quoted"})," values into larger token streams:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"comptime {\n let x = quote { 1 + 2 };\n let y = quote { $x + 4 };\n}\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The value of ",(0,o.jsx)(n.code,{children:"y"})," above is now the token stream containing five tokens: ",(0,o.jsx)(n.code,{children:"1 + 2 + 4"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Note that to unquote something, a variable name ",(0,o.jsx)(n.em,{children:"must"})," follow the ",(0,o.jsx)(n.code,{children:"$"})," operator in a token stream.\nIf it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted."]}),"\n",(0,o.jsxs)(n.p,{children:["Unquoting can also be avoided by escaping the ",(0,o.jsx)(n.code,{children:"$"})," with a backslash:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"comptime {\n let x = quote { 1 + 2 };\n\n // y contains the four tokens: `$x + 4`\n let y = quote { \\$x + 4 };\n}\n"})}),"\n",(0,o.jsx)(n.h3,{id:"combining-tokens",children:"Combining Tokens"}),"\n",(0,o.jsxs)(n.p,{children:["Note that ",(0,o.jsx)(n.code,{children:"Quoted"})," is internally a series of separate tokens, and that all unquoting does is combine these token vectors.\nThis means that code which appears to append like a string actually appends like a vector internally:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"comptime {\n let x = 3;\n let q = quote { foo$x }; // This is [foo, 3], not [foo3]\n\n // Spaces are ignored in general, they're never part of a token\n assert_eq(q, quote { foo 3 });\n}\n"})}),"\n",(0,o.jsxs)(n.p,{children:["If you do want string semantics, you can use format strings then convert back to a ",(0,o.jsx)(n.code,{children:"Quoted"})," value with ",(0,o.jsx)(n.code,{children:".quoted_contents()"}),".\nNote that formatting a quoted value with multiple tokens will always insert a space between each token. If this is\nundesired, you'll need to only operate on quoted values containing a single token. To do this, you can iterate\nover each token of a larger quoted value with ",(0,o.jsx)(n.code,{children:".tokens()"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="concatenate-example" showLineNumbers ',children:'comptime fn concatenate(q1: Quoted, q2: Quoted) -> Quoted {\n assert(q1.tokens().len() <= 1);\n assert(q2.tokens().len() <= 1);\n\n f"{q1}{q2}".quoted_contents()\n}\n\n// The CtString type is also useful for a compile-time string of unbounded size\n// so that you can append to it in a loop.\
1ncomptime fn double_spaced(q: Quoted) -> CtString {\n let mut result = "".as_ctstring();\n\n for token in q.tokens() {\n if result != "".as_ctstring() {\n result = result.append_str(" ");\n }\n result = result.append_fmtstr(f"{token}");\n }\n\n result\n}\n\n#[test]\nfn concatenate_test() {\n comptime {\n let result = concatenate(quote { foo }, quote { bar });\n assert_eq(result, quote { foobar });\n\n let result = double_spaced(quote {foo bar 3}).as_quoted_str!();\n assert_eq(result, "foo bar 3");\n }\n}\n'})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L293-L326",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L293-L326"})})})}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"crate",children:"$crate"}),"\n",(0,o.jsxs)(n.p,{children:["A common case when we have a library exporting macro code is that if you quote ",(0,o.jsx)(n.code,{children:"foo::my_function()"}),", whether the\nfunction can resolve will depend on the imports of where the macro code is used. It isn't enough to specify the\nfull path either. ",(0,o.jsx)(n.code,{children:"crate::foo::my_function()"})," will not work in external crates and ",(0,o.jsx)(n.code,{children:"my_crate_name::foo::my_function()"}),"\nwill not work if the external crate renames the dependency ",(0,o.jsx)(n.code,{children:"my_function"})," was defined in."]}),"\n",(0,o.jsxs)(n.p,{children:["For cases like this there is ",(0,o.jsx)(n.code,{children:"$crate"})," which when used in a quote will always resolve to the crate the quote is in.\nSo the library author can instead quote ",(0,o.jsx)(n.code,{children:"$crate::foo::my_function()"})," and have it work in all cases as long as\n",(0,o.jsx)(n.code,{children:"foo"})," and ",(0,o.jsx)(n.code,{children:"my_function"})," are both publicly visible."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"/// We want to access this function within the quoted code below\n/// and we want it to work in external crates.\npub fn double(x: u64) -> u64 { x * 2 }\n\ncomptime fn double_twice(code: Quoted) -> Quoted {\n quote {\n // `$crate` is a stand-in for the current crate\n $crate::double($crate::double($code))\n }\n}\n"})}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h2,{id:"attributes",children:"Attributes"}),"\n",(0,o.jsxs)(n.p,{children:["Attributes provide a way to run a ",(0,o.jsx)(n.code,{children:"comptime"})," function on an item in the program.\nWhen you use an attribute, the function with the same name will be called with that item as an argument:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:'#[my_struct_attribute]\nstruct Foo {}\n\ncomptime fn my_struct_attribute(s: TypeDefinition) {\n println("Called my_struct_attribute!");\n}\n\n#[my_function_attribute]\nfn foo() {}\n\ncomptime fn my_function_attribute(f: FunctionDefinition) {\n println("Called my_function_attribute!");\n}\n'})}),"\n",(0,o.jsxs)(n.p,{children:["Anything returned from one of these functions will be inserted at top-level along with the original item.\nNote that expressions are not valid at top-level so you'll get an error trying to return ",(0,o.jsx)(n.code,{children:"3"})," or similar just as if you tried to write a program containing ",(0,o.jsx)(n.code,{children:"3; struct Foo {}"}),".\nYou can insert other top-level items such as trait impls, structs, or functions this way though.\nFor example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="derive-field-count-example" showLineNumbers ',children:"trait FieldCount {\n fn field_count() -> u32;\n}\n\n#[derive_field_count]\nstruct Bar {\n x: Field,\n y: [Field; 2],\n}\n\ncomptime fn derive_field_count(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n let field_count = s.fields_as_written().len();\n quote {\n impl FieldCount for $typ {\n fn field_count() -> u32 {\n $field_count\n }\n }\n }\n}\n"})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L180-L202",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L180-L202"})})})}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"calling-annotations-with-additional-arguments",children:"Calling annotations with additional arguments"}),"\n",(0,o.jsx)(n.p,{children:"Arguments may optionally be given to attributes.\nWhen this is done, these additional arguments are passed to the attribute function after the item argument."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="annotation-arguments-example" showLineNumbers ',children:"#[assert_field_is_type(quote { i32 }.as_type())]\nstruct MyStruct {\n my_field: i32,\n}\n\ncomptime fn assert_field_is_type(s: TypeDefinition, typ: Type) {\n // Assert the first field in `s` has type `typ`\n let fields = s.fields([]);\n assert_eq(fields[0].typ(), typ);\n}\n"})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L204-L215",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L204-L215"})})})}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["If an argument is a path and the corresponding parameter is typed ",(0,o.jsx)(n.code,{children:"TraitDefinition"})," or\n",(0,o.jsx)(n.code,{children:"FunctionDefinition"}),", the path is resolved to that trait or function rather than evaluated as a\nvalue. This lets an attribute receive a function (or trait) by n
1ame and inspect it:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"#[validate(MyType::check)]\nstruct MyType {\n x: u32,\n}\n\nimpl MyType {\n fn check(self) {\n assert(self.x < 100);\n }\n}\n\ncomptime fn validate(_s: TypeDefinition, method: FunctionDefinition) {\n // `method` is the resolved definition of `MyType::check`, so its signature can be inspected.\n assert(method.parameters().len() == 1);\n}\n"})}),"\n",(0,o.jsxs)(n.p,{children:["We can also take any number of arguments by adding the ",(0,o.jsx)(n.code,{children:"varargs"})," attribute:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="annotation-varargs-example" showLineNumbers ',children:"#[assert_three_args(1, 2, 3)]\nstruct MyOtherStruct {\n my_other_field: u32,\n}\n\n#[varargs]\ncomptime fn assert_three_args(_s: TypeDefinition, args: [Field]) {\n assert_eq(args.len(), 3);\n}\n"})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L217-L227",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L217-L227"})})})}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"attribute-evaluation-order",children:"Attribute Evaluation Order"}),"\n",(0,o.jsxs)(n.p,{children:["Unlike the evaluation order of stray ",(0,o.jsx)(n.code,{children:"comptime {}"})," blocks within functions, attributes have a well-defined evaluation\norder. Within a module, attributes are evaluated top to bottom. Between modules, attributes in child modules are evaluated\nfirst. Attributes in sibling modules are resolved following the ",(0,o.jsx)(n.code,{children:"mod foo; mod bar;"})," declaration order within their parent\nmodules."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"mod foo; // attributes in foo are run first\nmod bar; // followed by attributes in bar\n\n// followed by any attributes in the parent module\n#[derive(Eq)]\nstruct Baz {}\n"})}),"\n",(0,o.jsx)(n.p,{children:"Here's a more involved example showing evaluation order:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"// Assume `nth` asserts this is the `nth` attribute which runs.\n// In this case, it is the third which runs due to the inline submodule further below\n#[nth(2)]\nmod child1;\n\n#[nth(3)]\npub fn foo() {}\n\n#![nth(4)]\n\n// This attribute is on a submodule rather than in it, so it is ordered with the rest of the parent module\n#[nth(5)]\nmod child2 {\n #![nth(0)]\n\n #[nth(1)]\n pub fn foo() {}\n}\n\n#[nth(6)]\npub fn bar() {}\n\n#![nth(7)]\n"})}),"\n",(0,o.jsx)(n.p,{children:"Note that because of this evaluation order, you may get an error trying to derive a trait for a struct whose fields\nhave not yet had the trait derived already:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"// Error! `Bar` field of `Foo` does not (yet) implement Eq!\n#[derive(Eq)]\nstruct Foo {\n bar: Bar\n}\n\n#[derive(Eq)]\nstruct Bar {}\n"})}),"\n",(0,o.jsx)(n.p,{children:"In this case, the issue can be resolved by rearranging the structs."}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h2,{id:"comptime-api",children:"Comptime API"}),"\n",(0,o.jsxs)(n.p,{children:["Although ",(0,o.jsx)(n.code,{children:"comptime"}),", ",(0,o.jsx)(n.code,{children:"quote"}),", and unquoting provide a flexible base for writing macros,\nNoir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API.\nThis API can be accessed through built-in functions in ",(0,o.jsx)(n.code,{children:"std::meta"})," as well as on methods of several ",(0,o.jsx)(n.code,{children:"comptime"})," types."]}),"\n",(0,o.jsxs)(n.p,{children:["The following is an incomplete list of some ",(0,o.jsx)(n.code,{children:"comptime"})," types along with some useful methods on them. You can see more in the standard library ",(0,o.jsx)(n.a,{href:"../libraries/standard_library/meta",children:"Metaprogramming section"}),"."]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"Quoted"}),": A token stream"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"Type"}),": The type of a Noir type\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"fn implements(self, constraint: TraitConstraint) -> bool"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Returns true if ",(0,o.jsx)(n.code,{children:"self"})," implements the given trait constraint"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"Expr"}),": A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured.\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Methods:\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"fn as_function_call(self) -> Option<FunctionCallExpression>"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"If this is a function call expression, return its function and arguments"}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"fn as_block(self) -> Option<[Expr]>"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"If this is a block, return each statement in the block"}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"FunctionDefinition"}),": A function definition\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Methods:\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"fn parameters(self) ->
1 [FunctionParameter]"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Returns each parameter's name and type"}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"TypeDefinition"}),": A struct or enum definition\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Methods:\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"fn as_type(self) -> Type"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Returns this ",(0,o.jsx)(n.code,{children:"TypeDefinition"})," as a ",(0,o.jsx)(n.code,{children:"Type"}),". Any generics are kept as-is"]}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"fn generics(self) -> [TypeGeneric]"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Return each generic parameter on this type"}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"fn fields(self) -> [StructField]"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Return the name, type, and visibility of each field"}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"TraitConstraint"}),": A trait constraint such as ",(0,o.jsx)(n.code,{children:"From<Field>"})]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"TypedExpr"}),": A type-checked expression."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"UnresolvedType"}),": A syntactic notation that refers to a Noir type that hasn't been resolved yet"]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["There are many more functions available by exploring the ",(0,o.jsx)(n.code,{children:"std::meta"})," module and its submodules.\nUsing these methods is the key to writing powerful metaprogramming libraries."]}),"\n",(0,o.jsx)(n.h3,{id:"use_callers_scope",children:(0,o.jsx)(n.code,{children:"#[use_callers_scope]"})}),"\n",(0,o.jsxs)(n.p,{children:["Since certain functions such as ",(0,o.jsx)(n.code,{children:"Quoted::as_type"}),", ",(0,o.jsx)(n.code,{children:"Expression::as_type"}),", or ",(0,o.jsx)(n.code,{children:"Quoted::as_trait_constraint"})," will attempt\nto resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default\nthese functions will resolve in the current function's scope which is usually the attribute function they are called in.\nIf you're working on a library however, this may be a completely different module or crate to the item you're trying to\nuse the attribute on. If you want to be able to use ",(0,o.jsx)(n.code,{children:"Quoted::as_type"})," to refer to types local to the caller's scope for\nexample, you can annotate your attribute function with ",(0,o.jsx)(n.code,{children:"#[use_callers_scope]"}),". This will ensure your attribute, and any\nclosures it uses, can refer to anything in the caller's scope. ",(0,o.jsx)(n.code,{children:"#[use_callers_scope]"})," also works recursively. So if both\nyour attribute function and a helper function it calls use it, then they can both refer to the same original caller."]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h2,{id:"example-derive",children:"Example: Derive"}),"\n",(0,o.jsxs)(n.p,{children:["Using all of the above, we can write a ",(0,o.jsx)(n.code,{children:"derive"})," macro that behaves similarly to Rust's but is not built into the language.\nFrom the user's perspective it will look like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",children:"// Example usage\n#[derive(Default, Eq, Ord)]\nstruct MyStruct { my_field: u32 }\n"})}),"\n",(0,o.jsxs)(n.p,{children:["To implement ",(0,o.jsx)(n.code,{children:"derive"})," we'll have to create a ",(0,o.jsx)(n.code,{children:"comptime"})," function that accepts\na variable amount of traits."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="derive_example" showLineNumbers ',children:"// These are needed for the unconstrained hashmap we're using to store derive functions\nuse crate::collections::umap::UHashMap;\nuse crate::hash::BuildHasherDefault;\nuse crate::hash::poseidon2::Poseidon2Hasher;\n\n// A derive function is one that given a type definition can\n// create us a quoted trait impl from it.\npub comptime type DeriveFunction = fn(TypeDefinition) -> Quoted;\n\n// We'll keep a global HANDLERS map to keep track of the derive handler for each trait\ncomptime mut global HANDLERS: UHashMap<TraitDefinition, DeriveFunction, Build
1HasherDefault<Poseidon2Hasher>> =\n UHashMap::default();\n\n// Given a type definition and a vector of traits to derive, create trait impls for each.\n// This function is as simple as iterating over the vector, checking if we have a trait\n// handler registered for the given trait, calling it, and appending the result.\n#[varargs]\npub comptime fn derive(s: TypeDefinition, traits: [TraitDefinition]) -> Quoted {\n let mut result = quote {};\n\n for trait_to_derive in traits {\n let handler = HANDLERS.get(trait_to_derive);\n assert(handler.is_some(), f\"No derive function registered for `{trait_to_derive}`\");\n\n let trait_impl = handler.unwrap()(s);\n result = quote { $result $trait_impl };\n }\n\n result\n}\n"})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L60-L93",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L60-L93"})})})}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Registering a derive function could be done as follows:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="derive_via" showLineNumbers ',children:'// To register a handler for a trait, just add it to our handlers map\npub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) {\n assert(HANDLERS.insert(t, f), f"A derive_via function has already been registered for {t}");\n}\n'})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L95-L102",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L95-L102"})})})}),"\n"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rust",metastring:'title="big-derive-usage-example" showLineNumbers ',children:"// Finally, to register a handler we call the above function as an annotation\n// with our handler function.\n#[derive_via(derive_do_nothing)]\ntrait DoNothing {\n fn do_nothing(self);\n}\n\ncomptime fn derive_do_nothing(s: TypeDefinition) -> Quoted {\n // This is simplified since we don't handle generics or where clauses!\n // In a real example we'd likely also need to introduce each of\n // `s.generics()` as well as a trait constraint for each generic\n // to ensure they also implement the trait.\n let typ = s.as_type();\n quote {\n impl DoNothing for $typ {\n fn do_nothing(self) {\n // Traits can't tell us what to do\n println(\"something\");\n }\n }\n }\n}\n\n// Since `DoNothing` is a simple trait which:\n// 1. Only has one method\n// 2. Does not have any generics on the trait itself\n// We can use `std::meta::make_trait_impl` to help us out.\n// This helper function will generate our impl for us along with any\n// necessary where clauses and still provides a flexible interface\n// for us to work on each field on the struct.\ncomptime fn derive_do_nothing_alt(s: TypeDefinition) -> Quoted {\n let trait_name = quote { DoNothing };\n let method_signature = quote { fn do_nothing(self) };\n\n // Call `do_nothing` recursively on each field in the struct\n let for_each_field = |field_name| quote { self.$field_name.do_nothing(); };\n\n // Some traits like Eq want to join each field expression with something like `&`.\n // We don't need that here\n let join_fields_with = quote {};\n\n // The body function is a spot to insert any extra setup/teardown needed.\n // We'll insert our println here. Since we recur on each field, we should see\n // one println for the struct itself, followed by a println for every field (recursively).\n let body = |body| quote {\n println(\"something\");\n $body\n };\n crate::meta::make_trait_impl(\n s,\n trait_name,\n method_signature,\n for_each_field,\n join_fields_with,\n body,\n )\n}\n"})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.sup,{children:(0,o.jsx)(n.sub,{children:(0,o.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L229-L287",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L229-L287"})})})}),"\n"]}),"\n",(0,o.jsx)(n.hr,{}),"\n",(0,o.jsx)(n.h2,{id:"security-considerations",children:"Security considerations"}),"\n",(0,o.jsx)(n.p,{children:"Comptime code in a dependency runs as part of your build and can shape the program that ultimately\ngets compiled. Two patterns in particular deserve attention before deploying a program that uses\nthird-party comptime code."}),"\n",(0,o.jsxs)(n.h3,{id:"comptime-functions-that-return-quoted",children:["Comptime functions that return ",(0,o.jsx)(n.code,{children:"Quoted"})]}),"\n",(0,o.jsxs)(n.p,{children:["A ",(0,o.jsx)(n.code,{children:"Quoted"})," value is a token stream that becomes program source when it is unquoted or returned from\nan attribute macro. A comptime function in a dependency that returns ",(0,o.jsx)(n.code,{children:"Quoted"})," to your crate \u2014 or an\nattribute macro it provides that you apply to one of your own items \u2014 can introduce arbitrary items,\nexpressions, or trait impls into your crate. The injected code runs as if you had written it\nyourself, even though it does not appear in your source files."]}),"\n",(0,o.jsxs)(n.p,{children:["Returning ",(0,o.jsx)(n.code,{children:"Quoted"})," is a deliberate feature of the metaprogramming API and is what makes macros\nuseful. The risk is not the feature itself but trusting comptime output from code you have not\nreviewed. When using third-party comptime code:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["Treat any attribute macro a dependency exposes, and any ",(0,o.jsx)(n.code,{children:"Quoted"})," value it returns to you, as code\nyou are vendoring into your crate."]}),"\n",(0,o.jsxs)(n.li,{children:["Run ",(0,o.jsx)(n.code,{children:"nargo expand"})," on your package after pulling in or upgrading an untrusted dependency, and\nreview the exp
1anded output. The expanded source is the code that actually compiles into your\ncircuit, and is the only authoritative view of what a macro inserted."]}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"functiondefinitiondisable",children:(0,o.jsx)(n.code,{children:"FunctionDefinition::disable"})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.a,{href:"/docs/v1.0.0-rc.0/libraries/standard_library/meta/function_def#disable",children:(0,o.jsx)(n.code,{children:"disable"})})," method lets a comptime function\nmark another function as un-callable. A disabled function does not fail at compile time: it fails\nonly when something tries to call it, with the error message that was passed to ",(0,o.jsx)(n.code,{children:"disable"}),". A\nmalicious or careless dependency that calls ",(0,o.jsx)(n.code,{children:"disable"})," on one of your functions (for example, a\ncontract entry point) can therefore take that functionality offline without producing any build-time\nsignal."]}),"\n",(0,o.jsx)(n.p,{children:"To catch this before deployment:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Exercise every externally reachable function \u2014 every contract entry point, every public API your\ncallers will hit \u2014 at least once in your test suite. A disabled function errors the first time\nit is invoked, so even a minimal smoke test of each endpoint is enough to detect this case."}),"\n",(0,o.jsxs)(n.li,{children:["Run ",(0,o.jsx)(n.code,{children:"nargo expand"})," on your package and inspect the expanded source for ",(0,o.jsx)(n.code,{children:"disable"})," calls on\nfunctions that you did not intend to disable."]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},28453(e,n,t){t.d(n,{R:()=>s,x:()=>a});var i=t(96540);const o={},r=i.createContext(o);function s(e){const n=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.