Metaprogramming
Partly written. §1 was begun with milestone M8a of the Lyric 5 plan (
@Test), §2–§4 with M9a (comptime,@When,std.meta); the rest comes with M9b, spec-first: each rule lands here with its conformance case before or with its implementation. Source of the decisions: 09 Metaprogramming.
Scope
Attributes (data, compiler instructions, macros), comptime, std.meta, conditional compilation with @When, macros over std.syntax.
1. Attributes
Conformance. conformance/cases/11-metaprogramming/; what lyric test makes of a test, the
toolchain's own tests (tests/Lyric5.Tests, TestCommandTests).
- An attribute is a struct that declares a marker for where it may sit (rule 4), and a use
writes its fields:
@Test { skip = "needs a network" }. An attribute with exactly one field without a default also takes that field's value in parentheses,@Retry(3); with any other number the form is refused (LYR-SEM0094). The compiler reads the attributes of a closed list instd.coreby their identity:@Testisstd.core.Test— a struct of the program's own namedTestis data, nothing the compiler reads. @Test— in the prelude — marks a test, whichlyric testruns (14 §3): a function that takes no parameters, returnsvoidand is notprivate(LYR-SEM0154), and is not generic (LYR-SEM0067, as for every attribute but the compiler instructions of rule 7). It may throw: an error that leaves it fails the test.skipsays why the test is not run,timeouthow many seconds it may take (60 unless it says; 0 for no limit).@callerExpr(p)— in the prelude — sits on a parameter: where a call leaves that parameter out, it gets the text the call wrote forp, another parameter of the function —assertEq(xs.length(), 3)hands the assertion the wordsxs.length()— as it is written, the parentheses in it and around it included:(1..5).sum(),(a + b). The parameter is astringand has a default, which a call that writes the argument itself keeps; the attribute names its parameter by name (LYR-SEM0155). Beside it a parameter takes what its marker admits there (rule 4).- Every declaration takes attributes (09 T7): the module header (
module;, §3 rule 4), a function, a method, a field (a variant's payload's too), astatic let, a variant, a parameter, a struct, a class, an enum, an interface, an alias, anextendblock, a module-level binding, an associated type. A marker names one kind or a set of them (09 F1):OnModule;OnFunction(a function at the top level),OnMethod,OnField,OnParameter,OnVariant;OnType(a struct, a class, an enum),OnInterface,OnAlias,OnExtend; and the setsOnCallable(a function, a method),OnMember(a method, a field, astatic let, a variant, an associated type),OnTypeLike(a struct, a class, an enum, an interface, an alias),OnTopLevel(what stands at a module's top level),OnDecl(every named declaration) andOnAny(every place). The markers inherit nothing from each other. An attribute none of whose markers admits the declaration it stands on is refused (LYR-SEM0065). - An attribute's value is computed while the program compiles (09 F4): its fields are
values as a
comptimesite's are (§2) — literals,lets, calls:@Test { timeout = LIMIT }. The value is the struct the use writes, its defaults filled, and what the compiler and the tools read is that value. A field of a type no literal makes is refused (LYR-SEM0100); a panic while computing it isLYR-CT0001. - An attribute makes nothing reachable (01 B13): a function only an attribute marks is not
built. A test is reached from the program
lyric testwrites. - The compiler instructions — in the prelude — are the closed list of
std.corethe compiler reads (09 A11, T2):@Test(rule 2) and@Bench(12 §Benchmarks),@callerExpr(rule 3),@callerLineand@callerFile(rule 8),@Deprecated(rule 9),@AlwaysInline,@NoInlineand@Cold(rule 10),@MustUse(rule 11),@NonExhaustive(rule 12),@Allow,@Warnand@Deny(rule 13),@Shared(rule 14),@SeesConformer(rule 15),@Layout(rule 16),@Export(rule 17) and@When(§3). What one says holds for every instance, so beside@Testthey sit on a generic declaration too, where data may not (LYR-SEM0067). @callerLineon anintparameter and@callerFileon astringone: where a call leaves the parameter out, it gets the line the call begins on, or the file the call stands in — the package's name, then the path within the package, with/on every system:app/src/main.lyr. A call through an interface gets them as a direct call does. The parameter has a default, which a call that writes the argument keeps (LYR-SEM0155, as for another type).@Deprecated { message, until, replacement }sits on what a use names: a module, what stands at a module's top level, a member — not a parameter (LYR-SEM0065); on anextendblock it deprecates the block's members. Every use outside a deprecated declaration warns (LYR-SEM0076) withmessage, and namesreplacementas what to write instead.untilnames the release that removes the declaration: a toolchain that claims that version or a later one refuses the attribute (LYR-SEM0081), as it refuses anuntilthat is no version,"5.1"or"5.1.2". Each field is a value computed as rule 5 says.@AlwaysInline,@NoInlineand@Coldon a function or a method are hints to the C compiler —always_inline,noinline,cold—, no promise: the program computes what it computes without them.@NoInlinekeeps the compiler's own inlining away too. A function takes@AlwaysInlineor@NoInline, not both (LYR-SEM0177).@MustUseon a function or a type: an expression statement that drops what such a function gives, or a value of such a type, warns (LYR-SEM0178);let _ = …drops a value on purpose, and avoidcall gives nothing to use. In the standard libraryResultcarries it, andstd.task.spawn, whose handle alone carries the task's error.@NonExhaustiveon an enum or a sealed interface: outside the package that declares it, amatchcovers it with a_or binding arm alone (LYR-SEM0050; 09 §3) — a later version may add to it; inside, its variants or conformers cover it.std.io.IoErrorKindcarries it.@Allow,@Warnand@Denywith a lint, its code, its group orwarnings—@Allow("unused-variable")— set the lint's level within the declaration they sit on: not said, a warning, an error (16 §5). An error that is no lint is never taken back. An@Allowthat takes back nothing warns (useless-allow,LYR-SEM0180), and a selector that names nothing is refused (LYR-SEM0179).@Sharedon a field: theclone()aClonesynthesis writes copies the field instead of cloning it (05 §14 rule 2) — the reference, where the field holds one. Aclone()the type writes itself is its own.@SeesConformeron an interface: its defaults ask with the module of the conformer they are set in place for (§4 rule 10), and so see every field of it, whatever its word. std.core's family and std.codec'sEncodeandDecodecarry it; an interface of the program that builds or reads a conformer whole — a parser of arguments, a codec — says so with it, and its documentation says that it does (the sweep after M10, W10).@Layout(C)on a struct or an enum,@Layout(uint8)and the other integer types on an enum: the type is laid out as C's and crosses into C (13 §3). Its form is a name, as@callerExpr's; a generic type does not take it, since one layout is not one per instance.@Export("geo_area")on apub fn, or a bare@Export: C calls the function by that symbol, or by the function's own name (13 §4). Its form is a string written out, which the header and the linker read before anything is computed; a generic function does not take it, since C has no type arguments.
2. comptime
Conformance. conformance/cases/11-metaprogramming/, the cases on comptime; that a site
computes what the binary computes — the number tower, the texts of numbers, strings, containers,
the error paths — in the toolchain's own tests (tests/Lyric5.Tests, ComptimeTests).
comptime eevaluatesewhile the program is compiled, and the site stands for the value, a literal of its type. The value is of any value type: a scalar, astring, and an array, an inline array, a tuple, an optional, a struct, an enum or aBoxof such. An array is the program's to write: every evaluation of the site gives a fresh one, as an array literal does. An object of a class, a view, a function value, a coroutine and an interface value are no literal (LYR-SEM0100, with the reason).esees what the module level sees and what it binds itself: it names no local or parameter of the function it stands in and nothis, and assigns nothing it did not bind — a module's variable is the run's, not the build's (LYR-SEM0100).- The code evaluated is pure (Q3):
ecalls functions, which then run at compile time, and builds values of any type on its way. A call that reaches the world — the console, files, the network, the clock, randomness, processes, threads and tasks — is an error at the site (LYR-CT0003) that names the call and the calls that led to it. - A panic of the evaluation is an error at the site (
LYR-CT0001). It names the run-time code the program would have ended with and shows the frames of the evaluation, innermost first (Q6). - The value is the binary's (Q7):
ecomputes as the compiled program does, in the profile it is built in — an overflow panics where the profile checks (03 T2) and wraps where it does not —, and alike on every platform the compiler runs on. - Each site has a budget of steps (Q5): the toolchain's, or
comptimeBudgetof the root manifest (15 §1 rule 7). A site that takes more is an error at the site (LYR-CT0002) that names the budget. What one step is belongs to the implementation. The standard library's sites take the toolchain's budget whatever the manifest says: a program's budget is its own code's, and std's tables are no part of it. comptime { … }is a block evaluated whole, its last expression the value. It binds, assigns, loops and builds lambdas as any block does.comptime if (c) { … } else { … }chooses while compiling:cis a site of typebool, and anelse ifafter it chooses the same way. Every branch is checked; only the chosen one is part of the program, so a function only another branch calls is not built. Aletcondition is refused (LYR-SEM0100).comptime for (x in w) { … }unrolls:wis a site holding an array, and the body, checked once, stands once per element, withxthat element's value. Acontinuegoes on with the next round, abreakpast the last. A pattern in place ofx, or awthat is no array, is refused (LYR-SEM0100).embed(path)(std.core) gives the bytes of a file as auint8[], read while the program compiles (Q4).pathis relative to the module that calls, and the file lies within the package's directory; a module of no package is held to its own directory. A file outside, or none, isLYR-CT0004. It is the one thing code run at compile time reads from outside the program, and a build reads the file again when it changes. Acomptimesite, or what one calls, makes the call; a call the program would make at run time is refused (LYR-CT0004).- A module binding's site is evaluated where the program reaches the binding (09 D8; M9a S12b): reached from the program's own roots, from the sites outside bindings, and through the initializer of a binding that is reached. A binding nobody reaches is not evaluated — its site may reach the world or exceed the budget without an error, as unreached code may panic —, and the program carries nothing of it. The standard library's tables cost a build only where it reads them.
3. Target, profile, @When
Conformance. conformance/cases/11-metaprogramming/; what a cross-build emits, the toolchain's
own tests (tests/Lyric5.Tests, TargetTests).
targetandprofile—std.core, in the prelude — are constants of the build (B4):target.os(Platform:Linux,MacOS,Windows,Other),target.arch(Arch:X86_64,Aarch64,Other) andtarget.pointerWidth(in bits) are the target's — a cross-build's, not the host's —, andprofile.debugholds where the profile is the built-indebugor inherits from it (15 §4). A program that reads neither carries neither.comptime if (target.os == .Windows)chooses by them while compiling (§2 rule 7): every branch is checked, the chosen one is built.@When(cond)— in the prelude — on what stands at a module's top level, or on the module (rule 4):condis aboolcomputed while compiling (§1 rule 5), overtargetandprofile. Where it holds, the declaration is built. Where it does not, the declaration is checked as every other, itscomptimesites are not evaluated, and nothing of it is built. A use the program would run of what a build leaves out is refused (LYR-CT0006); a use within what it leaves out, or in the branch acomptime ifdoes not take, is none.@Whenchooses no name: two declarations of one name stay refused (LYR-RES0001,LYR-SEM0085), andcomptime ifchooses between names.- The module header
module;names nothing — the path names the module (04 §2 rule 2) — and carries the module's attributes:@When(target.os == .Windows) module;leaves the whole module out of every other build. One module per target is a file per target, chosen between withcomptime if.
4. Type information
Conformance. conformance/cases/11-metaprogramming/.
std.metaanswers questions about a type as constants (R1) — namesstd.coredeclares andstd.metapasses on (09 S10), soimport std.meta { fields }andstd.core's name are one:fields<T>(), aFieldInfo { name, type, index, visibility, isVar, hasDefault }per field of a struct or a class, in declaration order,indexits place in the answer,hasDefaultwhether the declaration writes an initializer — the type's own default counts for nothing; a payload's field as a struct's (the sweep after M10, W9, X2);variants<E>(), aVariantInfo { name, index, payload, value }per variant, the payload's fields named by their position where they have no names,valuethe integer C holds for the variant of an enum with@Layout(13 §3 rule 5) and its index in any other;members<T>(), aMemberInfo { name, visibility, isStatic, isMut, kind, parameters }per function, constant and associated type of the type's own body, in declaration order —kindaMemberKind(Function,Constant,AssociatedType),parametersa function'sParameterInfo { name, type, index, hasDefault }, none of a constant or an associated type, which are static (the sweep after M10, X4);conformances<T>()andhasConformance<T, I>();typeName<T>();isStruct,isClass,isEnum,isInterface<T>(). ATypeholds its name and itskind, aTypeKind:Primitive(the scalars,string),Struct,Class,Enum,Interface,Optional,Array,InlineArray,Slice(a view),Tuple,Function,Parameter(a type parameter),Other(W9). Each answer is a constant of the type at the instance it is asked at — in a generic function or a default, of each instance (R3). Nothing is reflected at run time (R6): a program reads a type through such constants alone.- What a question sees (R7) is what the code that asks may name: the module of the function
it stands in, the interface's for a default. Another package's field is listed where it is
pub, one of the package where it is notprivate, one of the module always; members alike. comptime for (f in fields<T>())walks the answer (§2), its body once per field. WhereTis a type the loop's place knows — a written type, orSelfin a default (rule 5) — each round has the body as its own:fis that field's info, a constant acomptimesite of the round may read (comptime if (f.isVar)), ande.[f]is the field ofethatfnames, of the field's own type — read, written and a place ase.nameis (R2).eis a value of the typefwalks the fields of (LYR-SEM0185).T.[f]in a type's place is the type of that field —variants<Self.[f]>()walks an enum field's variants,fields<T.[f]>()a struct field's fields,let x: T.[f] = e.[f]binds it —,Tthe type the round walks; anywhere elseLYR-SEM0185(the sweep after M10, X5). WhereTis known only per instance — a type parameter of a generic function — the rounds share one body, checked once:fis aFieldInfo, and no field is read through it (LYR-SEM0185).attribute<A>(f)is the attribute of typeAthat the fieldfnames carries, its value, ornull(R4);fis a round's variable (LYR-SEM0185) — of a default's round overSelf: a generic helper overTwithcomptime for (f in fields<T>())does not read attributes (LYR-SEM0185there too), so a synthesis keeps its attribute reads in the defaults (M10 S2). A payload's field carries attributes as a struct's does, and a round overv.payloadreads them.variantAttribute<A>(v)is the one the variantvcarries,vthe variable of a round overvariants<E>();memberAttribute<A>(m)the one the membermcarries,mthe variable of a round overmembers<T>():comptime for (m in members<T>())has a body per member as rule 3 has one per field, whereTis a type the loop's place knows and the body reads throughm. In such a rounde.[m](…)calls the membermnames one, a value of the type the round walks, andT.[m](…)a static one onT: the call by the member's name,e.name(…), among overloads the arguments choose, checked in the round as it stands — outside such a round, or on a value of another type,LYR-SEM0185(the sweep after M10, X3).comptime for (p in m.parameters)in such a round has a body per parameter of the functionmnames,pthat parameter's info, andparameterAttribute<A>(p)is the attribute it carries (X4). Each reader asks its own loop's variable (LYR-SEM0185; the sweep after M10, W6).typeAttribute<T, A>()is the oneT's declaration carries — written as an alias, the alias's own, not its target's. What stands at a module's top level — its header, a function, anextend, a binding — no question of std.meta reads: a macro reads it and its attributes through std.syntax (§5; X4).- A default that walks
Self's fields is set in place for each conformer that takes it (A5): checked as the conformer's —Selfandthisthe conformer, its names those of the interface's module —, each round'sthis.[f]of the conformer's field's type. What is wrong there is said at the line written once, with the field and the conformer named. A call through a value of the interface reaches it; a conformer that writes the member itself takes nothing (05 §14 rule 1). A generic conformer's field of a parameter's type has what the parameter's constraints give it:struct Pair<T :: [ToJson]> :: [ToJson]is written out,Pair<T>without the constraint is refused at the default's line. A static default is set in place the same way (05 §2 rule 2), reached through the conformer's name or a constraint. - A
comptimesite is evaluated once: one that asksstd.metaabout a type known only per instance within a larger expression is refused (LYR-SEM0100); asked alone,comptime fields<T>(), the question is answered per instance. comptime for (v in variants<E>())walks an enum's variants (M9a S10), its body once per variant whereEis a type the loop's place knows,vthat variant's info. In such a rounde is vistruewhere the enum valueeholds the variant — a test of the run, abool—;E.[v]is the value of a unit variant;E.[v](…)builds a variant with a tuple payload andE.[v] { … }one with a struct payload, its fields by name or by info (rule 8);comptime for (p in v.payload)walks the payload's fields, its body once per field withpthat field's info, ande.[v, p]is that field ofe, of the field's own type — read, written and a place ase.nameis.e.[v, p]whereeholds another variant is a panic. Each form outside such rounds, or over a type known only per instance, isLYR-SEM0185.- An initializer over infos. In an initializer of a struct or a class — a written type
without type arguments, or
Selfin a default —[f] = esets the field the round's variablefnames, andcomptime for (f in fields<T>()) [f] = esets one field per round,echecked withfthat field's info and against the field's type. The fields set are the written ones (LYR-SEM0070twice,LYR-SEM0015none such, an omitted one as for a written initializer); the rounds walk the fields of the type built (LYR-SEM0185otherwise). The same items build a variant's struct payload inE.[v] { … }, overv.payload. - A
comptime ifa round decides checks the chosen branch alone — against §2 rule 7, which checks every branch: the other may name what this field's type has not, aclone()of a@Sharedfield. A round decides a condition made of its variables'isVar,index,nameandhasDefault, a variant'sindexandname, a member'sname,isStatic,isMutandkindagainst aMemberKind, a parameter'sname,indexandhasDefault,attribute<A>(f),variantAttribute<A>(v),memberAttribute<A>(m)andparameterAttribute<A>(p)againstnull,isEnum<T>()and its kin of a known type, a field's type'sname(f.type.name) and itskindagainst aTypeKind(f.type.kind == .Optional; W9), a payload'slength(),startsWithover such a string, literals, and==,!=,<,<=,>,>=,!,&&and||over them (M10 S3).comptime if (c) a else bis a value too: where a round decidesc, the chosen arm alone is checked and is the value; otherwise both arms, of one type (LYR-SEM0100), and the site chooses. - An interface marked
@SeesConformerasks with the conformer's module (§1 rule 15; the sweep after M10, W10). The defaults of such an interface — std.core'sEquatable,Hashable,Ordered,TotalOrder,Clone,VariantNameandVariants(05 §14), std.codec'sEncodeandDecode(12 §Codecs rule 7; M10 S2), and a program's that carries the marker — see every field of the conformer, whatever its word: a type's internal and private fields compare, hash, clone and serialize as itspubones do, and a library's parser fills them. Every other interface's default sees what rule 2 gives it. What is wrong in one of std.core's family's is said at the conformer's entry (05 §14 rule 6). A write through an info,this.[f] = …, answers to the field's word and the function'smutas a write to the field by name does (LYR-SEM0019). - Enum reflection is synthesis (09 A7 R5; M9a S11):
VariantNamegives any enumname(), the name of the variant the value holds;Variants :: [VariantName]gives an enum whose variants all are unitsall(), every variant in declaration order, andfromName(s): ?Self, the variant of that name ornull— std.core's defaults overvariants<Self>(), taken on request (enum E :: [Variants]), both named in the prelude. A variant with a payload has no value without it:Variantson such an enum isLYR-SEM0135at the entry, the variant named. (all, notvariants: inside the default the latter is the question itself.)
5. Macros
A macro (design 09 A8, Q9; 08 Y12; 13 M9b) is a function over syntax that the compiler runs at a
site's expansion, under the interpreter's rules of §2. This section is written
slice by slice: the declaration and the sites (M9b S2), quote and #{…} with hygiene (S3),
attribute macros (S4), the diagnostics of expanded code (S5); lyric expand is
14 §1.1 rule 6.
- A macro is a function over syntax.
macro name(params): Ret { … }stands at the top of a module as a function does,puband imported like one. Its parameters and its result are types ofstd.syntax(12 §Syntax) —Expr,Block,Ident,Literal,Type,Stmt,Stmt[],Decl,Decl[]— elseLYR-SEM0186; it has no type parameters, since what a site hands over is a tree, not a value with a type. A macro is never called as a function and never a value (LYR-SEM0187): its name stands at a site, with the!. - A site expands the macro.
name!(args),name!(args) { … }andname! { … }are the three forms: the!directly behind the name, before(or{, is the expansion; with a space between, or before anything else, it is the unwrap postfix of 08. Each argument is syntax as written, checked nowhere, and reaches the macro as the value of its parameter's type: an expression asExpr, a block asBlockorStmt[], a name asIdent, a literal asLiteral, a path asType; the trailing block goes to a lastBlockorStmt[]parameter. The count and the kinds of the arguments are checked at the site (LYR-SEM0191). - The result stands in place of the site. An
Exprreplaces a site that is an expression; aStmt,Stmt[]orBlockreplaces the statement a site is on its own (name!(…) { … }on its line ends at its block, as a call with a trailing block does) — elsewhereLYR-SEM0188. What the result names is resolved where the site stands: a name the macro builds withIdent.ofis the caller's; what aquotewrote follows rule 8. A site inside an argument travels into the macro as syntax (Expr.Macro) and is expanded in the round after the outer one. - Expansion is a phase per module, in import order. A macro expands in the modules that
import it, before the module is checked, and the module that declares it checks it first: a
site of a macro of its own module is refused (
LYR-SEM0189), a site of a name that is no macro (LYR-SEM0190). A module whose sites are not expanded yet waits for the macros it declares to be used; an expansion that has not ended after 32 rounds — a result holding a site again and again — isLYR-CT0007. A program without a site pays nothing. - The macro runs under the interpreter's rules (§2; 09 Q9): pure, with the
budget, a panic
LYR-CT0001at the site with the chain of calls.error(at, msg)ofstd.syntaxis the macro's own error,LYR-MAC0001: the main span at the node the macro names (e.span(), an argument's as written), a note at the site's!. quote { … }is a tree as a value. Its content is syntax, checked nowhere: statements — a last expression without;is the tail, and a quote of a lone tail is anExpr— or declarations when the block opens with one. The quote's type is its content's kind,Expr,Stmt[],Block,DeclorDecl[]; where the content allows two, the type the place expects decides (Stmt[]orBlockfor a lone tail,Declfor a lone declaration). It needsstd.syntaxin the program (LYR-SEM0194).#{e}splices a value into the content. The expression is the function's code, run at the quote, and its value — of a type ofstd.syntax— stands in the tree at the splice's place: where an expression stands, anExpr, anIdent(as a name), aLiteralor aBlock(as a block expression); where a statement stands on its own,Stmt[]and aBlock's statements are spliced in, aStmtstands, an expression, a name or a literal is the statement or the tail; where a declaration's name stands (fn #{name}()), anIdent; where a type stands (v: #{t}), aType. Anything else at a place isLYR-SEM0193. A splice statement ends at its}, as a block does.- A quote is hygienic. Every name the content binds — a
let, a parameter, a loop variable, acatch's binding, a pattern's names — is fresh at every evaluation of the quote (Ident.fresh): the caller's names are never caught, and the names of two quotes are two. What#{…}sets in is the caller's, as written. A free name of the content — a function, a type, a constant the content names and does not bind — is the macro's module's: it is resolved there, with that module's imports, wherever the tree ends up, though the site's module never imported it (theIdent'sfrom, theNamedtype's). A name built withIdent.ofis the site's, as in rule 3. - An attribute macro is a macro whose first parameter is a declaration (09 A2 F2): a
StructDecl,ClassDecl,FnDecl,EnumDecl,InterfaceDecl,ExtendDecl,LetDecl, orDeclfor any; its result is aDeclor aDecl[](LYR-SEM0186). It sits on a top-level declaration as an attribute, with no!(T4):@Builder struct P { … },@Retry(3) fn f() { … }. The declaration reaches the macro as the first argument, without this attribute and with the rest; the attribute's positional argument fills the parameters after it, by their kinds as at a site — fields (@A { x = 1 }) are a data attribute's and refused (LYR-SEM0191); a declaration of another kind than the first parameter's is refused (LYR-SEM0191). - The result stands in the declaration's place (T5): a
Declone for one, aDecl[]spliced into the module where the declaration stood — the declaration itself only when the macro gives it back. Nothing else of the module is touched: an attribute never changes code that does not carry it. A result that carries an attribute macro again is expanded in the round after (rule 4); the module that declares the macro cannot carry it (LYR-SEM0189). A#{t}in type position splices aType(rule 7), which a declaration macro needs. - An error in expanded code is said at the site. What a macro gives back stands in a file
of its own, synthesized from the result as source, whose origin is the site: an error the
check finds in it is reported at the site —
name!(…), or the@Name— with the message prefixedin the expansion of 'name!', a notesynthesized as:with the line of the expansion it stood on, and, where the result came from aquote, a note at the quote in the macro's module. What the caller wrote keeps its place: an argument's node stands where it was written, through the macro and back, so an error in it is the caller's, at its line, and a warning in it stays; a warning in the macro's own code is nobody's and falls. A site that an expansion holds chains: the notes of the inner expansion first, then the outer's, and the message names both. One mistake is said once, though a macro spliced the argument twice.error(at, msg)stays rule 5's; a panic in the macro stays §2's.